about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/lib.rs14
-rw-r--r--compiler/rustc_ast/src/ast.rs18
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs31
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl5
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs15
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs39
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs120
-rw-r--r--compiler/rustc_ast_passes/messages.ftl3
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs21
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs11
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs2
-rw-r--r--compiler/rustc_ast_passes/src/show_span.rs6
-rw-r--r--compiler/rustc_attr/src/builtin.rs2
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs15
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs115
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs29
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs33
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs6
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs33
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/mod.rs1
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs26
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs21
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs7
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs10
-rw-r--r--compiler/rustc_builtin_macros/src/test.rs12
-rw-r--r--compiler/rustc_builtin_macros/src/type_ascribe.rs2
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs10
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs7
-rw-r--r--compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs3
-rw-r--r--compiler/rustc_codegen_gcc/src/asm.rs5
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/gcc_util.rs4
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/allocator.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/asm.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs7
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs11
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs18
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/type_of.rs33
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs50
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs14
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs15
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs39
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs187
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs17
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs19
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs15
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs6
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs10
-rw-r--r--compiler/rustc_const_eval/src/errors.rs86
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs2
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/mod.rs1
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs46
-rw-r--r--compiler/rustc_data_structures/src/fx.rs1
-rw-r--r--compiler/rustc_data_structures/src/graph/dominators/mod.rs1
-rw-r--r--compiler/rustc_data_structures/src/sorted_map.rs2
-rw-r--r--compiler/rustc_data_structures/src/stable_hasher.rs26
-rw-r--r--compiler/rustc_data_structures/src/svh.rs2
-rw-r--r--compiler/rustc_data_structures/src/sync.rs7
-rw-r--r--compiler/rustc_data_structures/src/unord.rs128
-rw-r--r--compiler/rustc_driver_impl/Cargo.toml1
-rw-r--r--compiler/rustc_driver_impl/src/args.rs107
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs13
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs2
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0733.md26
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0796.md22
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0797.md26
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs14
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs58
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs458
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs63
-rw-r--r--compiler/rustc_errors/src/emitter.rs28
-rw-r--r--compiler/rustc_errors/src/json.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs324
-rw-r--r--compiler/rustc_expand/Cargo.toml1
-rw-r--r--compiler/rustc_expand/src/base.rs4
-rw-r--r--compiler/rustc_expand/src/config.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs39
-rw-r--r--compiler/rustc_expand/src/mbe/quoted.rs6
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs81
-rw-r--r--compiler/rustc_expand/src/module.rs2
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs18
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs8
-rw-r--r--compiler/rustc_expand/src/tests.rs4
-rw-r--r--compiler/rustc_feature/src/unstable.rs2
-rw-r--r--compiler/rustc_hir/src/hir.rs74
-rw-r--r--compiler/rustc_hir/src/intravisit.rs9
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml1
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl14
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs58
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs160
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs57
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs199
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs43
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/errs.rs97
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs86
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs96
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/unsafety.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs53
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs38
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs25
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs20
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs22
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs97
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs24
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs179
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs3
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs73
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs187
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs68
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs43
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/gather_locals.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/inherited.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs27
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs76
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs66
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs12
-rw-r--r--compiler/rustc_hir_typeck/src/upvar.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs16
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/work_product.rs4
-rw-r--r--compiler/rustc_index/src/bit_set.rs24
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs34
-rw-r--r--compiler/rustc_infer/src/errors/note_and_explain.rs8
-rw-r--r--compiler/rustc_infer/src/infer/canonical/canonicalizer.rs91
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs32
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs7
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs8
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/table.rs5
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs7
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs16
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs31
-rw-r--r--compiler/rustc_interface/src/callbacks.rs6
-rw-r--r--compiler/rustc_interface/src/passes.rs8
-rw-r--r--compiler/rustc_interface/src/queries.rs7
-rw-r--r--compiler/rustc_interface/src/tests.rs10
-rw-r--r--compiler/rustc_interface/src/util.rs8
-rw-r--r--compiler/rustc_lint/src/builtin.rs62
-rw-r--r--compiler/rustc_lint/src/errors.rs2
-rw-r--r--compiler/rustc_lint/src/foreign_modules.rs10
-rw-r--r--compiler/rustc_lint/src/internal.rs4
-rw-r--r--compiler/rustc_lint/src/let_underscore.rs5
-rw-r--r--compiler/rustc_lint/src/levels.rs2
-rw-r--r--compiler/rustc_lint/src/lib.rs6
-rw-r--r--compiler/rustc_lint/src/lints.rs127
-rw-r--r--compiler/rustc_lint/src/non_ascii_idents.rs11
-rw-r--r--compiler/rustc_lint/src/non_fmt_panic.rs11
-rw-r--r--compiler/rustc_lint/src/types.rs3
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs128
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs12
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp4
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs18
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs14
-rw-r--r--compiler/rustc_macros/src/diagnostics/utils.rs6
-rw-r--r--compiler/rustc_macros/src/lib.rs2
-rw-r--r--compiler/rustc_macros/src/serialize.rs16
-rw-r--r--compiler/rustc_metadata/src/creader.rs41
-rw-r--r--compiler/rustc_metadata/src/errors.rs22
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs168
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs36
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs130
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs4
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/lint.rs23
-rw-r--r--compiler/rustc_middle/src/middle/mod.rs15
-rw-r--r--compiler/rustc_middle/src/middle/region.rs29
-rw-r--r--compiler/rustc_middle/src/middle/resolve_bound_vars.rs6
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs14
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs5
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs10
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs11
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs45
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs11
-rw-r--r--compiler/rustc_middle/src/mir/spanview.rs642
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs19
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs8
-rw-r--r--compiler/rustc_middle/src/query/keys.rs13
-rw-r--r--compiler/rustc_middle/src/query/mod.rs37
-rw-r--r--compiler/rustc_middle/src/query/on_disk_cache.rs227
-rw-r--r--compiler/rustc_middle/src/query/plumbing.rs4
-rw-r--r--compiler/rustc_middle/src/thir.rs11
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs11
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs12
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs27
-rw-r--r--compiler/rustc_middle/src/ty/opaque_types.rs2
-rw-r--r--compiler/rustc_middle/src/ty/parameterized.rs7
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs78
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs6
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs2
-rw-r--r--compiler/rustc_middle/src/ty/util.rs76
-rw-r--r--compiler/rustc_middle/src/values.rs161
-rw-r--r--compiler/rustc_mir_build/src/build/custom/parse/instruction.rs12
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs29
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs4
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs80
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs111
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs10
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs17
-rw-r--r--compiler/rustc_mir_build/src/errors.rs8
-rw-r--r--compiler/rustc_mir_build/src/lib.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs38
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs9
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs40
-rw-r--r--compiler/rustc_mir_build/src/thir/print.rs25
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs11
-rw-r--r--compiler/rustc_mir_transform/src/const_goto.rs2
-rw-r--r--compiler/rustc_mir_transform/src/const_prop_lint.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs18
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs90
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs140
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs189
-rw-r--r--compiler/rustc_mir_transform/src/deduplicate_blocks.rs2
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs37
-rw-r--r--compiler/rustc_mir_transform/src/early_otherwise_branch.rs2
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs27
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs5
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs22
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs32
-rw-r--r--compiler/rustc_mir_transform/src/lint.rs62
-rw-r--r--compiler/rustc_mir_transform/src/match_branches.rs2
-rw-r--r--compiler/rustc_mir_transform/src/pass_manager.rs12
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs (renamed from compiler/rustc_const_eval/src/transform/promote_consts.rs)399
-rw-r--r--compiler/rustc_mir_transform/src/remove_unneeded_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/separate_const_switch.rs4
-rw-r--r--compiler/rustc_mir_transform/src/simplify.rs92
-rw-r--r--compiler/rustc_mir_transform/src/ssa.rs1
-rw-r--r--compiler/rustc_mir_transform/src/unreachable_prop.rs2
-rw-r--r--compiler/rustc_monomorphize/messages.ftl3
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs6
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs7
-rw-r--r--compiler/rustc_monomorphize/src/partitioning.rs2
-rw-r--r--compiler/rustc_parse/messages.ftl7
-rw-r--r--compiler/rustc_parse/src/errors.rs37
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs114
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs6
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs4
-rw-r--r--compiler/rustc_parse/src/lib.rs8
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs41
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs72
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs233
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs24
-rw-r--r--compiler/rustc_parse/src/parser/item.rs97
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs13
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs80
-rw-r--r--compiler/rustc_parse/src/parser/path.rs4
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs6
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs21
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs6
-rw-r--r--compiler/rustc_parse_format/src/lib.rs28
-rw-r--r--compiler/rustc_passes/messages.ftl5
-rw-r--r--compiler/rustc_passes/src/check_attr.rs6
-rw-r--r--compiler/rustc_passes/src/check_const.rs2
-rw-r--r--compiler/rustc_passes/src/dead.rs58
-rw-r--r--compiler/rustc_passes/src/debugger_visualizer.rs2
-rw-r--r--compiler/rustc_passes/src/errors.rs46
-rw-r--r--compiler/rustc_passes/src/hir_id_validator.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs26
-rw-r--r--compiler/rustc_passes/src/stability.rs19
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs6
-rw-r--r--compiler/rustc_pattern_analysis/src/lib.rs19
-rw-r--r--compiler/rustc_pattern_analysis/src/lints.rs106
-rw-r--r--compiler/rustc_pattern_analysis/src/pat.rs103
-rw-r--r--compiler/rustc_pattern_analysis/src/rustc.rs192
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs106
-rw-r--r--compiler/rustc_query_impl/src/lib.rs6
-rw-r--r--compiler/rustc_query_impl/src/plumbing.rs6
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs14
-rw-r--r--compiler/rustc_query_system/src/query/config.rs4
-rw-r--r--compiler/rustc_query_system/src/query/job.rs2
-rw-r--r--compiler/rustc_query_system/src/query/mod.rs11
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs27
-rw-r--r--compiler/rustc_query_system/src/values.rs11
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs24
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs95
-rw-r--r--compiler/rustc_resolve/src/imports.rs10
-rw-r--r--compiler/rustc_resolve/src/late.rs25
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs80
-rw-r--r--compiler/rustc_resolve/src/macros.rs19
-rw-r--r--compiler/rustc_serialize/tests/opaque.rs16
-rw-r--r--compiler/rustc_session/src/config.rs30
-rw-r--r--compiler/rustc_session/src/errors.rs7
-rw-r--r--compiler/rustc_session/src/options.rs42
-rw-r--r--compiler/rustc_session/src/parse.rs6
-rw-r--r--compiler/rustc_session/src/session.rs47
-rw-r--r--compiler/rustc_smir/src/rustc_internal/internal.rs9
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs22
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs14
-rw-r--r--compiler/rustc_span/src/def_id.rs65
-rw-r--r--compiler/rustc_span/src/hygiene.rs93
-rw-r--r--compiler/rustc_span/src/lib.rs349
-rw-r--r--compiler/rustc_span/src/source_map.rs12
-rw-r--r--compiler/rustc_span/src/source_map/tests.rs2
-rw-r--r--compiler/rustc_span/src/span_encoding.rs47
-rw-r--r--compiler/rustc_span/src/symbol.rs27
-rw-r--r--compiler/rustc_symbol_mangling/src/errors.rs4
-rw-r--r--compiler/rustc_target/src/abi/mod.rs4
-rw-r--r--compiler/rustc_target/src/asm/s390x.rs29
-rw-r--r--compiler/rustc_target/src/spec/mod.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs30
-rw-r--r--compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs3
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs17
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs35
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs33
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs64
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs118
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs83
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs24
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs20
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs2
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs1
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs11
-rw-r--r--compiler/rustc_type_ir/Cargo.toml2
-rw-r--r--compiler/rustc_type_ir/src/codec.rs6
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs6
-rw-r--r--compiler/stable_mir/src/lib.rs22
-rw-r--r--compiler/stable_mir/src/ty.rs15
381 files changed, 6403 insertions, 6092 deletions
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 549927d5898..ea194e10def 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -16,7 +16,7 @@ use rustc_data_structures::stable_hasher::StableOrd;
 #[cfg(feature = "nightly")]
 use rustc_macros::HashStable_Generic;
 #[cfg(feature = "nightly")]
-use rustc_macros::{Decodable, Encodable};
+use rustc_macros::{Decodable_Generic, Encodable_Generic};
 #[cfg(feature = "nightly")]
 use std::iter::Step;
 
@@ -30,7 +30,7 @@ pub use layout::LayoutCalculator;
 pub trait HashStableContext {}
 
 #[derive(Clone, Copy, PartialEq, Eq, Default)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub struct ReprFlags(u8);
 
 bitflags! {
@@ -52,7 +52,7 @@ bitflags! {
 rustc_data_structures::external_bitflags_debug! { ReprFlags }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub enum IntegerType {
     /// Pointer-sized integer type, i.e. `isize` and `usize`. The field shows signedness, e.g.
     /// `Pointer(true)` means `isize`.
@@ -73,7 +73,7 @@ impl IntegerType {
 
 /// Represents the repr options provided by the user.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub struct ReprOptions {
     pub int: Option<IntegerType>,
     pub align: Option<Align>,
@@ -412,7 +412,7 @@ impl FromStr for Endian {
 
 /// Size of a type in bytes.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub struct Size {
     raw: u64,
 }
@@ -636,7 +636,7 @@ impl Step for Size {
 
 /// Alignment of a type in bytes (always a power of two).
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub struct Align {
     pow2: u8,
 }
@@ -777,7 +777,7 @@ impl AbiAndPrefAlign {
 
 /// Integers, also used for enum discriminants.
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
-#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_Generic))]
+#[cfg_attr(feature = "nightly", derive(Encodable_Generic, Decodable_Generic, HashStable_Generic))]
 pub enum Integer {
     I8,
     I16,
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 1812cc335a4..e1e4e5fc567 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -20,6 +20,7 @@
 
 pub use crate::format::*;
 pub use crate::util::parser::ExprPrecedence;
+pub use rustc_span::AttrId;
 pub use GenericArgs::*;
 pub use UnsafeSource::*;
 
@@ -30,7 +31,6 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::sync::Lrc;
 use rustc_macros::HashStable_Generic;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 use rustc_span::source_map::{respan, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
@@ -2682,22 +2682,6 @@ pub enum AttrStyle {
     Inner,
 }
 
-rustc_index::newtype_index! {
-    #[orderable]
-    #[debug_format = "AttrId({})"]
-    pub struct AttrId {}
-}
-
-impl<S: Encoder> Encodable<S> for AttrId {
-    fn encode(&self, _s: &mut S) {}
-}
-
-impl<D: Decoder> Decodable<D> for AttrId {
-    default fn decode(_: &mut D) -> AttrId {
-        panic!("cannot decode `AttrId` with `{}`", std::any::type_name::<D>());
-    }
-}
-
 /// A list of attributes.
 pub type AttrVec = ThinVec<Attribute>;
 
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index 4c0c496584e..298c01a4567 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -21,12 +21,12 @@ use crate::AttrVec;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_macros::HashStable_Generic;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
-use rustc_span::{sym, Span, Symbol, DUMMY_SP};
+use rustc_serialize::{Decodable, Encodable};
+use rustc_span::{sym, Span, SpanDecoder, SpanEncoder, Symbol, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
 use std::borrow::Cow;
-use std::{cmp, fmt, iter, mem};
+use std::{cmp, fmt, iter};
 
 /// When the main Rust parser encounters a syntax-extension invocation, it
 /// parses the arguments to the invocation as a token tree. This is a very
@@ -81,14 +81,6 @@ impl TokenTree {
         }
     }
 
-    /// Modify the `TokenTree`'s span in-place.
-    pub fn set_span(&mut self, span: Span) {
-        match self {
-            TokenTree::Token(token, _) => token.span = span,
-            TokenTree::Delimited(dspan, ..) => *dspan = DelimSpan::from_single(span),
-        }
-    }
-
     /// Create a `TokenTree::Token` with alone spacing.
     pub fn token_alone(kind: TokenKind, span: Span) -> TokenTree {
         TokenTree::Token(Token::new(kind, span), Spacing::Alone)
@@ -158,14 +150,14 @@ impl fmt::Debug for LazyAttrTokenStream {
     }
 }
 
-impl<S: Encoder> Encodable<S> for LazyAttrTokenStream {
+impl<S: SpanEncoder> Encodable<S> for LazyAttrTokenStream {
     fn encode(&self, s: &mut S) {
         // Used by AST json printing.
         Encodable::encode(&self.to_attr_token_stream(), s);
     }
 }
 
-impl<D: Decoder> Decodable<D> for LazyAttrTokenStream {
+impl<D: SpanDecoder> Decodable<D> for LazyAttrTokenStream {
     fn decode(_d: &mut D) -> Self {
         panic!("Attempted to decode LazyAttrTokenStream");
     }
@@ -461,19 +453,6 @@ impl TokenStream {
         t1.next().is_none() && t2.next().is_none()
     }
 
-    /// Applies the supplied function to each `TokenTree` and its index in `self`, returning a new `TokenStream`
-    ///
-    /// It is equivalent to `TokenStream::new(self.trees().cloned().enumerate().map(|(i, tt)| f(i, tt)).collect())`.
-    pub fn map_enumerated_owned(
-        mut self,
-        mut f: impl FnMut(usize, TokenTree) -> TokenTree,
-    ) -> TokenStream {
-        let owned = Lrc::make_mut(&mut self.0); // clone if necessary
-        // rely on vec's in-place optimizations to avoid another allocation
-        *owned = mem::take(owned).into_iter().enumerate().map(|(i, tree)| f(i, tree)).collect();
-        self
-    }
-
     /// Create a token stream containing a single token with alone spacing. The
     /// spacing used for the final token in a constructed stream doesn't matter
     /// because it's never used. In practice we arbitrarily use
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index fd94e7e9341..e7177402db1 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -35,7 +35,7 @@ ast_lowering_bad_return_type_notation_output =
 
 ast_lowering_base_expression_double_dot =
     base expression required after `..`
-    .label = add a base expression here
+    .suggestion = add a base expression here
 
 ast_lowering_clobber_abi_not_supported =
     `clobber_abi` is not supported on this target
@@ -106,7 +106,8 @@ ast_lowering_misplaced_double_dot =
     .note = only allowed in tuple, tuple struct, and slice patterns
 
 ast_lowering_misplaced_impl_trait =
-    `impl Trait` only allowed in function and inherent method argument and return types, not in {$position}
+    `impl Trait` is not allowed in {$position}
+    .note = `impl Trait` is only allowed in arguments and return types of functions and methods
 
 ast_lowering_misplaced_relax_trait_bound =
     `?Trait` bounds are only permitted at the point where a type parameter is declared
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 710690d0d86..2811fe104cd 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -90,6 +90,7 @@ pub enum AssocTyParenthesesSub {
 
 #[derive(Diagnostic)]
 #[diag(ast_lowering_misplaced_impl_trait, code = "E0562")]
+#[note]
 pub struct MisplacedImplTrait<'a> {
     #[primary_span]
     pub span: Span,
@@ -113,10 +114,10 @@ pub struct UnderscoreExprLhsAssign {
 }
 
 #[derive(Diagnostic, Clone, Copy)]
-#[diag(ast_lowering_base_expression_double_dot)]
+#[diag(ast_lowering_base_expression_double_dot, code = "E0797")]
 pub struct BaseExpressionDoubleDot {
     #[primary_span]
-    #[label]
+    #[suggestion(code = "/* expr */", applicability = "has-placeholders", style = "verbose")]
     pub span: Span,
 }
 
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index ba858d49acf..69704de105c 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -546,20 +546,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
     fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
         let pat = self.lower_pat(&arm.pat);
-        let guard = arm.guard.as_ref().map(|cond| {
-            if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
-                hir::Guard::IfLet(self.arena.alloc(hir::Let {
-                    hir_id: self.next_id(),
-                    span: self.lower_span(*span),
-                    pat: self.lower_pat(pat),
-                    ty: None,
-                    init: self.lower_expr(scrutinee),
-                    is_recovered: *is_recovered,
-                }))
-            } else {
-                hir::Guard::If(self.lower_expr(cond))
-            }
-        });
+        let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
         let hir_id = self.next_id();
         let span = self.lower_span(arm.span);
         self.lower_attrs(hir_id, &arm.attrs);
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index c618953461c..d8de447e5b4 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -12,6 +12,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
 use rustc_hir::PredicateOrigin;
 use rustc_index::{Idx, IndexSlice, IndexVec};
+use rustc_middle::span_bug;
 use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, sym, Ident};
@@ -182,7 +183,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
             }
             ItemKind::Static(box ast::StaticItem { ty: t, mutability: m, expr: e }) => {
-                let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
+                let (ty, body_id) =
+                    self.lower_const_item(t, span, e.as_deref(), ImplTraitPosition::StaticTy);
                 hir::ItemKind::Static(ty, *m, body_id)
             }
             ItemKind::Const(box ast::ConstItem { generics, ty, expr, .. }) => {
@@ -191,7 +193,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     Const::No,
                     id,
                     &ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
-                    |this| this.lower_const_item(ty, span, expr.as_deref()),
+                    |this| {
+                        this.lower_const_item(ty, span, expr.as_deref(), ImplTraitPosition::ConstTy)
+                    },
                 );
                 hir::ItemKind::Const(ty, generics, body_id)
             }
@@ -448,8 +452,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         ty: &Ty,
         span: Span,
         body: Option<&Expr>,
+        impl_trait_position: ImplTraitPosition,
     ) -> (&'hir hir::Ty<'hir>, hir::BodyId) {
-        let ty = self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::ConstTy));
+        let ty = self.lower_ty(ty, &ImplTraitContext::Disallowed(impl_trait_position));
         (ty, self.lower_const_body(span, body))
     }
 
@@ -572,23 +577,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // This is used to track which lifetimes have already been defined,
         // and which need to be replicated when lowering an async fn.
 
-        match parent_hir.node().expect_item().kind {
+        let generics = match parent_hir.node().expect_item().kind {
             hir::ItemKind::Impl(impl_) => {
                 self.is_in_trait_impl = impl_.of_trait.is_some();
+                &impl_.generics
             }
-            hir::ItemKind::Trait(_, _, generics, _, _) if self.tcx.features().effects => {
-                self.host_param_id = generics
-                    .params
-                    .iter()
-                    .find(|param| {
-                        matches!(
-                            param.kind,
-                            hir::GenericParamKind::Const { is_host_effect: true, .. }
-                        )
-                    })
-                    .map(|param| param.def_id);
+            hir::ItemKind::Trait(_, _, generics, _, _) => generics,
+            kind => {
+                span_bug!(item.span, "assoc item has unexpected kind of parent: {}", kind.descr())
             }
-            _ => {}
+        };
+
+        if self.tcx.features().effects {
+            self.host_param_id = generics
+                .params
+                .iter()
+                .find(|param| {
+                    matches!(param.kind, hir::GenericParamKind::Const { is_host_effect: true, .. })
+                })
+                .map(|param| param.def_id);
         }
 
         match ctxt {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index c7c77bf56b7..dc23b1dce7b 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -304,8 +304,6 @@ enum ImplTraitPosition {
     ClosureParam,
     PointerParam,
     FnTraitParam,
-    TraitParam,
-    ImplParam,
     ExternFnReturn,
     ClosureReturn,
     PointerReturn,
@@ -324,29 +322,27 @@ impl std::fmt::Display for ImplTraitPosition {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         let name = match self {
             ImplTraitPosition::Path => "paths",
-            ImplTraitPosition::Variable => "variable bindings",
+            ImplTraitPosition::Variable => "the type of variable bindings",
             ImplTraitPosition::Trait => "traits",
             ImplTraitPosition::AsyncBlock => "async blocks",
             ImplTraitPosition::Bound => "bounds",
             ImplTraitPosition::Generic => "generics",
-            ImplTraitPosition::ExternFnParam => "`extern fn` params",
-            ImplTraitPosition::ClosureParam => "closure params",
-            ImplTraitPosition::PointerParam => "`fn` pointer params",
-            ImplTraitPosition::FnTraitParam => "`Fn` trait params",
-            ImplTraitPosition::TraitParam => "trait method params",
-            ImplTraitPosition::ImplParam => "`impl` method params",
+            ImplTraitPosition::ExternFnParam => "`extern fn` parameters",
+            ImplTraitPosition::ClosureParam => "closure parameters",
+            ImplTraitPosition::PointerParam => "`fn` pointer parameters",
+            ImplTraitPosition::FnTraitParam => "the parameters of `Fn` trait bounds",
             ImplTraitPosition::ExternFnReturn => "`extern fn` return types",
             ImplTraitPosition::ClosureReturn => "closure return types",
             ImplTraitPosition::PointerReturn => "`fn` pointer return types",
-            ImplTraitPosition::FnTraitReturn => "`Fn` trait return types",
+            ImplTraitPosition::FnTraitReturn => "the return type of `Fn` trait bounds",
             ImplTraitPosition::GenericDefault => "generic parameter defaults",
             ImplTraitPosition::ConstTy => "const types",
             ImplTraitPosition::StaticTy => "static types",
             ImplTraitPosition::AssocTy => "associated types",
             ImplTraitPosition::FieldTy => "field types",
-            ImplTraitPosition::Cast => "cast types",
+            ImplTraitPosition::Cast => "cast expression types",
             ImplTraitPosition::ImplSelf => "impl headers",
-            ImplTraitPosition::OffsetOf => "`offset_of!` params",
+            ImplTraitPosition::OffsetOf => "`offset_of!` parameters",
         };
 
         write!(f, "{name}")
@@ -364,19 +360,6 @@ enum FnDeclKind {
     Impl,
 }
 
-impl FnDeclKind {
-    fn param_impl_trait_allowed(&self) -> bool {
-        matches!(self, FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait)
-    }
-
-    fn return_impl_trait_allowed(&self) -> bool {
-        match self {
-            FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => true,
-            _ => false,
-        }
-    }
-}
-
 #[derive(Copy, Clone)]
 enum AstOwner<'a> {
     NonOwner,
@@ -1434,19 +1417,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 let (bounds, lifetime_bound) = self.with_dyn_type_scope(true, |this| {
                     let bounds =
                         this.arena.alloc_from_iter(bounds.iter().filter_map(|bound| match bound {
-                            GenericBound::Trait(
-                                ty,
-                                TraitBoundModifiers {
-                                    polarity: BoundPolarity::Positive | BoundPolarity::Negative(_),
-                                    constness,
-                                },
-                            ) => Some(this.lower_poly_trait_ref(ty, itctx, *constness)),
-                            // We can safely ignore constness here, since AST validation
-                            // will take care of invalid modifier combinations.
-                            GenericBound::Trait(
-                                _,
-                                TraitBoundModifiers { polarity: BoundPolarity::Maybe(_), .. },
-                            ) => None,
+                            // We can safely ignore constness here since AST validation
+                            // takes care of rejecting invalid modifier combinations and
+                            // const trait bounds in trait object types.
+                            GenericBound::Trait(ty, modifiers) => match modifiers.polarity {
+                                BoundPolarity::Positive | BoundPolarity::Negative(_) => {
+                                    Some(this.lower_poly_trait_ref(
+                                        ty,
+                                        itctx,
+                                        // Still, don't pass along the constness here; we don't want to
+                                        // synthesize any host effect args, it'd only cause problems.
+                                        ast::BoundConstness::Never,
+                                    ))
+                                }
+                                BoundPolarity::Maybe(_) => None,
+                            },
                             GenericBound::Outlives(lifetime) => {
                                 if lifetime_bound.is_none() {
                                     lifetime_bound = Some(this.lower_lifetime(lifetime));
@@ -1840,19 +1825,19 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             inputs = &inputs[..inputs.len() - 1];
         }
         let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
-            let itctx = if kind.param_impl_trait_allowed() {
-                ImplTraitContext::Universal
-            } else {
-                ImplTraitContext::Disallowed(match kind {
-                    FnDeclKind::Fn | FnDeclKind::Inherent => {
-                        unreachable!("fn should allow APIT")
-                    }
-                    FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
-                    FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
-                    FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
-                    FnDeclKind::Trait => ImplTraitPosition::TraitParam,
-                    FnDeclKind::Impl => ImplTraitPosition::ImplParam,
-                })
+            let itctx = match kind {
+                FnDeclKind::Fn | FnDeclKind::Inherent | FnDeclKind::Impl | FnDeclKind::Trait => {
+                    ImplTraitContext::Universal
+                }
+                FnDeclKind::ExternFn => {
+                    ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnParam)
+                }
+                FnDeclKind::Closure => {
+                    ImplTraitContext::Disallowed(ImplTraitPosition::ClosureParam)
+                }
+                FnDeclKind::Pointer => {
+                    ImplTraitContext::Disallowed(ImplTraitPosition::PointerParam)
+                }
             };
             self.lower_ty_direct(&param.ty, &itctx)
         }));
@@ -1864,26 +1849,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             }
             None => match &decl.output {
                 FnRetTy::Ty(ty) => {
-                    let context = if kind.return_impl_trait_allowed() {
-                        let fn_def_id = self.local_def_id(fn_node_id);
-                        ImplTraitContext::ReturnPositionOpaqueTy {
-                            origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
+                    let itctx = match kind {
+                        FnDeclKind::Fn
+                        | FnDeclKind::Inherent
+                        | FnDeclKind::Trait
+                        | FnDeclKind::Impl => ImplTraitContext::ReturnPositionOpaqueTy {
+                            origin: hir::OpaqueTyOrigin::FnReturn(self.local_def_id(fn_node_id)),
                             fn_kind: kind,
+                        },
+                        FnDeclKind::ExternFn => {
+                            ImplTraitContext::Disallowed(ImplTraitPosition::ExternFnReturn)
+                        }
+                        FnDeclKind::Closure => {
+                            ImplTraitContext::Disallowed(ImplTraitPosition::ClosureReturn)
+                        }
+                        FnDeclKind::Pointer => {
+                            ImplTraitContext::Disallowed(ImplTraitPosition::PointerReturn)
                         }
-                    } else {
-                        ImplTraitContext::Disallowed(match kind {
-                            FnDeclKind::Fn
-                            | FnDeclKind::Inherent
-                            | FnDeclKind::Trait
-                            | FnDeclKind::Impl => {
-                                unreachable!("fn should allow return-position impl trait in traits")
-                            }
-                            FnDeclKind::ExternFn => ImplTraitPosition::ExternFnReturn,
-                            FnDeclKind::Closure => ImplTraitPosition::ClosureReturn,
-                            FnDeclKind::Pointer => ImplTraitPosition::PointerReturn,
-                        })
                     };
-                    hir::FnRetTy::Return(self.lower_ty(ty, &context))
+                    hir::FnRetTy::Return(self.lower_ty(ty, &itctx))
                 }
                 FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
             },
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index feea02c679c..a10797626f1 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -188,6 +188,9 @@ ast_passes_module_nonascii = trying to load file for module `{$name}` with non-a
 ast_passes_negative_bound_not_supported =
     negative bounds are not supported
 
+ast_passes_negative_bound_with_parenthetical_notation =
+    parenthetical notation may not be used for negative bounds
+
 ast_passes_nested_impl_trait = nested `impl Trait` is not allowed
     .outer = outer `impl Trait`
     .inner = nested `impl Trait` here
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index b69d4cccaf0..7f78f687055 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1312,13 +1312,24 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
         if let GenericBound::Trait(trait_ref, modifiers) = bound
             && let BoundPolarity::Negative(_) = modifiers.polarity
             && let Some(segment) = trait_ref.trait_ref.path.segments.last()
-            && let Some(ast::GenericArgs::AngleBracketed(args)) = segment.args.as_deref()
         {
-            for arg in &args.args {
-                if let ast::AngleBracketedArg::Constraint(constraint) = arg {
-                    self.dcx()
-                        .emit_err(errors::ConstraintOnNegativeBound { span: constraint.span });
+            match segment.args.as_deref() {
+                Some(ast::GenericArgs::AngleBracketed(args)) => {
+                    for arg in &args.args {
+                        if let ast::AngleBracketedArg::Constraint(constraint) = arg {
+                            self.dcx().emit_err(errors::ConstraintOnNegativeBound {
+                                span: constraint.span,
+                            });
+                        }
+                    }
+                }
+                // The lowered form of parenthesized generic args contains a type binding.
+                Some(ast::GenericArgs::Parenthesized(args)) => {
+                    self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
+                        span: args.span,
+                    });
                 }
+                None => {}
             }
         }
 
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 42ada39f515..fcf19ce52ec 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -725,8 +725,8 @@ impl AddToDiagnostic for StableFeature {
             rustc_errors::SubdiagnosticMessage,
         ) -> rustc_errors::SubdiagnosticMessage,
     {
-        diag.set_arg("name", self.name);
-        diag.set_arg("since", self.since);
+        diag.arg("name", self.name);
+        diag.arg("since", self.since);
         diag.help(fluent::ast_passes_stable_since);
     }
 }
@@ -764,6 +764,13 @@ pub struct ConstraintOnNegativeBound {
 }
 
 #[derive(Diagnostic)]
+#[diag(ast_passes_negative_bound_with_parenthetical_notation)]
+pub struct NegativeBoundWithParentheticalNotation {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(ast_passes_invalid_unnamed_field_ty)]
 pub struct InvalidUnnamedFieldTy {
     #[primary_span]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 2b746789a76..737e81eb6ec 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -23,7 +23,7 @@ macro_rules! gate {
     ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
         if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
             feature_err(&$visitor.sess.parse_sess, sym::$feature, $span, $explain)
-                .help($help)
+                .with_help($help)
                 .emit();
         }
     }};
diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs
index 9882f1d23ce..10590074282 100644
--- a/compiler/rustc_ast_passes/src/show_span.rs
+++ b/compiler/rustc_ast_passes/src/show_span.rs
@@ -38,21 +38,21 @@ struct ShowSpanVisitor<'a> {
 impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
     fn visit_expr(&mut self, e: &'a ast::Expr) {
         if let Mode::Expression = self.mode {
-            self.dcx.emit_warning(errors::ShowSpan { span: e.span, msg: "expression" });
+            self.dcx.emit_warn(errors::ShowSpan { span: e.span, msg: "expression" });
         }
         visit::walk_expr(self, e);
     }
 
     fn visit_pat(&mut self, p: &'a ast::Pat) {
         if let Mode::Pattern = self.mode {
-            self.dcx.emit_warning(errors::ShowSpan { span: p.span, msg: "pattern" });
+            self.dcx.emit_warn(errors::ShowSpan { span: p.span, msg: "pattern" });
         }
         visit::walk_pat(self, p);
     }
 
     fn visit_ty(&mut self, t: &'a ast::Ty) {
         if let Mode::Type = self.mode {
-            self.dcx.emit_warning(errors::ShowSpan { span: t.span, msg: "type" });
+            self.dcx.emit_warn(errors::ShowSpan { span: t.span, msg: "type" });
         }
         visit::walk_ty(self, t);
     }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 77678dcaba9..b3f601b7595 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -621,7 +621,7 @@ pub fn eval_condition(
                 }
             };
             let Some(min_version) = parse_version(*min_version) else {
-                dcx.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
+                dcx.emit_warn(session_diagnostics::UnknownVersionLiteral { span: *span });
                 return false;
             };
 
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index fd2b0866867..89606b81a99 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -54,13 +54,12 @@ pub(crate) struct UnknownMetaItem<'a> {
 impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for UnknownMetaItem<'_> {
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
-        let mut diag = DiagnosticBuilder::new(dcx, level, fluent::attr_unknown_meta_item);
-        diag.set_span(self.span);
-        diag.code(error_code!(E0541));
-        diag.set_arg("item", self.item);
-        diag.set_arg("expected", expected.join(", "));
-        diag.span_label(self.span, fluent::attr_label);
-        diag
+        DiagnosticBuilder::new(dcx, level, fluent::attr_unknown_meta_item)
+            .with_span(self.span)
+            .with_code(error_code!(E0541))
+            .with_arg("item", self.item)
+            .with_arg("expected", expected.join(", "))
+            .with_span_label(self.span, fluent::attr_label)
     }
 }
 
@@ -215,7 +214,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for UnsupportedLiteral {
                 }
             },
         );
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag.code(error_code!(E0565));
         if self.is_bytestr {
             diag.span_suggestion(
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 900b7891019..351976cdaea 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{struct_span_err, DiagCtxt, DiagnosticBuilder};
+use rustc_errors::{struct_span_code_err, DiagCtxt, DiagnosticBuilder};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 
@@ -31,17 +31,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         borrow_span: Span,
         borrow_desc: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0503,
             "cannot use {} because it was mutably borrowed",
             desc,
-        );
-
-        err.span_label(borrow_span, format!("{borrow_desc} is borrowed here"));
-        err.span_label(span, format!("use of borrowed {borrow_desc}"));
-        err
+        )
+        .with_span_label(borrow_span, format!("{borrow_desc} is borrowed here"))
+        .with_span_label(span, format!("use of borrowed {borrow_desc}"))
     }
 
     pub(crate) fn cannot_mutably_borrow_multiply(
@@ -54,7 +52,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'tcx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
             E0499,
@@ -100,7 +98,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_loan_span: Span,
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
             E0524,
@@ -133,7 +131,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_opt_via: &str,
         previous_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'cx> {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
             E0500,
@@ -165,7 +163,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         previous_end_span: Option<Span>,
         second_borrow_desc: &str,
     ) -> DiagnosticBuilder<'cx> {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
             E0501,
@@ -198,7 +196,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_load_end_span: Option<Span>,
     ) -> DiagnosticBuilder<'cx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0502,
@@ -238,17 +236,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         borrow_span: Span,
         desc: &str,
     ) -> DiagnosticBuilder<'cx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0506,
             "cannot assign to {} because it is borrowed",
             desc,
-        );
-
-        err.span_label(borrow_span, format!("{desc} is borrowed here"));
-        err.span_label(span, format!("{desc} is assigned to here but it was already borrowed"));
-        err
+        )
+        .with_span_label(borrow_span, format!("{desc} is borrowed here"))
+        .with_span_label(span, format!("{desc} is assigned to here but it was already borrowed"))
     }
 
     pub(crate) fn cannot_reassign_immutable(
@@ -258,11 +254,11 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         is_arg: bool,
     ) -> DiagnosticBuilder<'cx> {
         let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
-        struct_span_err!(self.dcx(), span, E0384, "cannot assign {} {}", msg, desc)
+        struct_span_code_err!(self.dcx(), span, E0384, "cannot assign {} {}", msg, desc)
     }
 
     pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(self.dcx(), span, E0594, "cannot assign to {}", desc)
+        struct_span_code_err!(self.dcx(), span, E0594, "cannot assign to {}", desc)
     }
 
     pub(crate) fn cannot_move_out_of(
@@ -270,7 +266,13 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         move_from_span: Span,
         move_from_desc: &str,
     ) -> DiagnosticBuilder<'cx> {
-        struct_span_err!(self.dcx(), move_from_span, E0507, "cannot move out of {}", move_from_desc)
+        struct_span_code_err!(
+            self.dcx(),
+            move_from_span,
+            E0507,
+            "cannot move out of {}",
+            move_from_desc
+        )
     }
 
     /// Signal an error due to an attempt to move out of the interior
@@ -287,16 +289,15 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             (&ty::Slice(_), _) => "slice",
             _ => span_bug!(move_from_span, "this path should not cause illegal move"),
         };
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             move_from_span,
             E0508,
             "cannot move out of type `{}`, a non-copy {}",
             ty,
             type_name,
-        );
-        err.span_label(move_from_span, "cannot move out of here");
-        err
+        )
+        .with_span_label(move_from_span, "cannot move out of here")
     }
 
     pub(crate) fn cannot_move_out_of_interior_of_drop(
@@ -304,15 +305,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         move_from_span: Span,
         container_ty: Ty<'_>,
     ) -> DiagnosticBuilder<'cx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             move_from_span,
             E0509,
             "cannot move out of type `{}`, which implements the `Drop` trait",
             container_ty,
-        );
-        err.span_label(move_from_span, "cannot move out of here");
-        err
+        )
+        .with_span_label(move_from_span, "cannot move out of here")
     }
 
     pub(crate) fn cannot_act_on_moved_value(
@@ -324,7 +324,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
     ) -> DiagnosticBuilder<'tcx> {
         let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
 
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             use_span,
             E0382,
@@ -341,7 +341,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         path: &str,
         reason: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(self.dcx(), span, E0596, "cannot borrow {} as mutable{}", path, reason)
+        struct_span_code_err!(
+            self.dcx(),
+            span,
+            E0596,
+            "cannot borrow {} as mutable{}",
+            path,
+            reason
+        )
     }
 
     pub(crate) fn cannot_mutate_in_immutable_section(
@@ -352,7 +359,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         immutable_section: &str,
         action: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             mutate_span,
             E0510,
@@ -360,10 +367,9 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             action,
             immutable_place,
             immutable_section,
-        );
-        err.span_label(mutate_span, format!("cannot {action}"));
-        err.span_label(immutable_span, format!("value is immutable in {immutable_section}"));
-        err
+        )
+        .with_span_label(mutate_span, format!("cannot {action}"))
+        .with_span_label(immutable_span, format!("value is immutable in {immutable_section}"))
     }
 
     pub(crate) fn cannot_borrow_across_coroutine_yield(
@@ -372,21 +378,20 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         yield_span: Span,
     ) -> DiagnosticBuilder<'tcx> {
         let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0626,
             "borrow may still be in use when {coroutine_kind:#} yields",
-        );
-        err.span_label(yield_span, "possible yield occurs here");
-        err
+        )
+        .with_span_label(yield_span, "possible yield occurs here")
     }
 
     pub(crate) fn cannot_borrow_across_destructor(
         &self,
         borrow_span: Span,
     ) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             borrow_span,
             E0713,
@@ -399,7 +404,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         path: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(self.dcx(), span, E0597, "{} does not live long enough", path,)
+        struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path,)
     }
 
     pub(crate) fn cannot_return_reference_to_local(
@@ -409,7 +414,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         reference_desc: &str,
         path_desc: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0515,
@@ -417,14 +422,11 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
             RETURN = return_kind,
             REFERENCE = reference_desc,
             LOCAL = path_desc,
-        );
-
-        err.span_label(
+        )
+        .with_span_label(
             span,
             format!("{return_kind}s a {reference_desc} data owned by the current function"),
-        );
-
-        err
+        )
     }
 
     pub(crate) fn cannot_capture_in_long_lived_closure(
@@ -435,23 +437,22 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         capture_span: Span,
         scope: &str,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             closure_span,
             E0373,
             "{closure_kind} may outlive the current {scope}, but it borrows {borrowed_path}, \
              which is owned by the current {scope}",
-        );
-        err.span_label(capture_span, format!("{borrowed_path} is borrowed here"))
-            .span_label(closure_span, format!("may outlive borrowed value {borrowed_path}"));
-        err
+        )
+        .with_span_label(capture_span, format!("{borrowed_path} is borrowed here"))
+        .with_span_label(closure_span, format!("may outlive borrowed value {borrowed_path}"))
     }
 
     pub(crate) fn thread_local_value_does_not_live_long_enough(
         &self,
         span: Span,
     ) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0712,
@@ -463,7 +464,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         span: Span,
     ) -> DiagnosticBuilder<'tcx> {
-        struct_span_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",)
+        struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",)
     }
 }
 
@@ -472,7 +473,7 @@ pub(crate) fn borrowed_data_escapes_closure<'tcx>(
     escape_span: Span,
     escapes_from: &str,
 ) -> DiagnosticBuilder<'tcx> {
-    struct_span_err!(
+    struct_span_code_err!(
         tcx.dcx(),
         escape_span,
         E0521,
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index d824260f47c..b4a73574aa2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,7 +1,9 @@
+// ignore-tidy-filelength
+
 use either::Either;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
@@ -550,8 +552,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         };
 
         let used = desired_action.as_general_verb_in_past_tense();
-        let mut err =
-            struct_span_err!(self.dcx(), span, E0381, "{used} binding {desc}{isnt_initialized}");
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            span,
+            E0381,
+            "{used} binding {desc}{isnt_initialized}"
+        );
         use_spans.var_path_only_subdiag(&mut err, desired_action);
 
         if let InitializationRequiringAction::PartialAssignment
@@ -2218,15 +2224,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             drop_span, borrow_span
         );
 
-        let mut err = self.thread_local_value_does_not_live_long_enough(borrow_span);
-
-        err.span_label(
-            borrow_span,
-            "thread-local variables cannot be borrowed beyond the end of the function",
-        );
-        err.span_label(drop_span, "end of enclosing function is here");
-
-        err
+        self.thread_local_value_does_not_live_long_enough(borrow_span)
+            .with_span_label(
+                borrow_span,
+                "thread-local variables cannot be borrowed beyond the end of the function",
+            )
+            .with_span_label(drop_span, "end of enclosing function is here")
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -3590,7 +3593,7 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
                                 ));
                             } else if let Some(guard) = &arm.guard {
                                 self.errors.push((
-                                    arm.pat.span.to(guard.body().span),
+                                    arm.pat.span.to(guard.span),
                                     format!(
                                         "if this pattern and condition are matched, {} is not \
                                          initialized",
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index f1e712d814a..6606be2f9f4 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -315,7 +315,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
             let mut failed = false;
 
             let elaborated_args = std::iter::zip(*args, &generics.params).map(|(arg, param)| {
-                if let Some(ty::Dynamic(obj, _, ty::DynKind::Dyn)) = arg.as_type().map(Ty::kind) {
+                if let Some(ty::Dynamic(obj, _, ty::Dyn)) = arg.as_type().map(Ty::kind) {
                     let default = tcx.object_lifetime_default(param.def_id);
 
                     let re_static = tcx.lifetimes.re_static;
@@ -339,7 +339,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
 
                     has_dyn = true;
 
-                    Ty::new_dynamic(tcx, obj, implied_region, ty::DynKind::Dyn).into()
+                    Ty::new_dynamic(tcx, obj, implied_region, ty::Dyn).into()
                 } else {
                     arg
                 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index f3b21d22c1a..fb3525e8998 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -329,15 +329,15 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         if let PlaceRef { local, projection: [] } = deref_base {
             let decl = &self.body.local_decls[local];
             if decl.is_ref_for_guard() {
-                let mut err = self.cannot_move_out_of(
-                    span,
-                    &format!("`{}` in pattern guard", self.local_names[local].unwrap()),
-                );
-                err.note(
-                    "variables bound in patterns cannot be moved from \
-                     until after the end of the pattern guard",
-                );
-                return err;
+                return self
+                    .cannot_move_out_of(
+                        span,
+                        &format!("`{}` in pattern guard", self.local_names[local].unwrap()),
+                    )
+                    .with_note(
+                        "variables bound in patterns cannot be moved from \
+                         until after the end of the pattern guard",
+                    );
             } else if decl.is_ref_to_static() {
                 return self.report_cannot_move_from_static(move_place, span);
             }
@@ -381,15 +381,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     closure_kind_ty, closure_kind, place_description,
                 );
 
-                let mut diag = self.cannot_move_out_of(span, &place_description);
-
-                diag.span_label(upvar_span, "captured outer variable");
-                diag.span_label(
-                    self.infcx.tcx.def_span(def_id),
-                    format!("captured by this `{closure_kind}` closure"),
-                );
-
-                diag
+                self.cannot_move_out_of(span, &place_description)
+                    .with_span_label(upvar_span, "captured outer variable")
+                    .with_span_label(
+                        self.infcx.tcx.def_span(def_id),
+                        format!("captured by this `{closure_kind}` closure"),
+                    )
             }
             _ => {
                 let source = self.borrowed_content_source(deref_base);
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index b3450b09cdf..8c8ca1ead40 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -27,7 +27,7 @@ use rustc_middle::ty::TypeVisitor;
 use rustc_middle::ty::{self, RegionVid, Ty};
 use rustc_middle::ty::{Region, TyCtxt};
 use rustc_span::symbol::{kw, Ident};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 
 use crate::borrowck_errors;
 use crate::session_diagnostics::{
@@ -84,7 +84,7 @@ impl<'tcx> RegionErrors<'tcx> {
     #[track_caller]
     pub fn push(&mut self, val: impl Into<RegionErrorKind<'tcx>>) {
         let val = val.into();
-        self.1.sess.dcx().span_delayed_bug(DUMMY_SP, format!("{val:?}"));
+        self.1.sess.dcx().delayed_bug(format!("{val:?}"));
         self.0.push(val);
     }
     pub fn is_empty(&self) -> bool {
@@ -348,7 +348,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     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);
-                    let mut diag = unexpected_hidden_region_diagnostic(
+                    let diag = unexpected_hidden_region_diagnostic(
                         self.infcx.tcx,
                         span,
                         named_ty,
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 093017ecba2..462b5c8da42 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -421,7 +421,7 @@ fn check_opaque_type_parameter_valid(
             return Err(tcx
                 .dcx()
                 .struct_span_err(span, "non-defining opaque type use in defining scope")
-                .span_note(spans, format!("{descr} used multiple times"))
+                .with_span_note(spans, format!("{descr} used multiple times"))
                 .emit());
         }
     }
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index 5bd7cc9514c..61b6bef3b87 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -94,31 +94,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             );
         }
 
-        debug!(
-            "equate_inputs_and_outputs: body.yield_ty {:?}, universal_regions.yield_ty {:?}",
-            body.yield_ty(),
-            universal_regions.yield_ty
-        );
-
-        // We will not have a universal_regions.yield_ty if we yield (by accident)
-        // outside of a coroutine and return an `impl Trait`, so emit a span_delayed_bug
-        // because we don't want to panic in an assert here if we've already got errors.
-        if body.yield_ty().is_some() != universal_regions.yield_ty.is_some() {
-            self.tcx().dcx().span_delayed_bug(
-                body.span,
-                format!(
-                    "Expected body to have yield_ty ({:?}) iff we have a UR yield_ty ({:?})",
-                    body.yield_ty(),
-                    universal_regions.yield_ty,
-                ),
+        if let Some(mir_yield_ty) = body.yield_ty() {
+            let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
+            self.equate_normalized_input_or_output(
+                universal_regions.yield_ty.unwrap(),
+                mir_yield_ty,
+                yield_span,
             );
         }
 
-        if let (Some(mir_yield_ty), Some(ur_yield_ty)) =
-            (body.yield_ty(), universal_regions.yield_ty)
-        {
+        if let Some(mir_resume_ty) = body.resume_ty() {
             let yield_span = body.local_decls[RETURN_PLACE].source_info.span;
-            self.equate_normalized_input_or_output(ur_yield_ty, mir_yield_ty, yield_span);
+            self.equate_normalized_input_or_output(
+                universal_regions.resume_ty.unwrap(),
+                mir_resume_ty,
+                yield_span,
+            );
         }
 
         // Return types are a bit more complex. They may contain opaque `impl Trait` types.
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
index dc4695fd2b0..e137bc1be0a 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
@@ -183,6 +183,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for LiveVariablesVisitor<'cx, 'tcx> {
         match ty_context {
             TyContext::ReturnTy(SourceInfo { span, .. })
             | TyContext::YieldTy(SourceInfo { span, .. })
+            | TyContext::ResumeTy(SourceInfo { span, .. })
             | TyContext::UserTy(span)
             | TyContext::LocalDecl { source_info: SourceInfo { span, .. }, .. } => {
                 span_bug!(span, "should not be visiting outside of the CFG: {:?}", ty_context);
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 80575e30a8d..9c0f53ddb86 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1450,13 +1450,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            TerminatorKind::Yield { value, .. } => {
+            TerminatorKind::Yield { value, resume_arg, .. } => {
                 self.check_operand(value, term_location);
 
-                let value_ty = value.ty(body, tcx);
                 match body.yield_ty() {
                     None => span_mirbug!(self, term, "yield in non-coroutine"),
                     Some(ty) => {
+                        let value_ty = value.ty(body, tcx);
                         if let Err(terr) = self.sub_types(
                             value_ty,
                             ty,
@@ -1474,6 +1474,28 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         }
                     }
                 }
+
+                match body.resume_ty() {
+                    None => span_mirbug!(self, term, "yield in non-coroutine"),
+                    Some(ty) => {
+                        let resume_ty = resume_arg.ty(body, tcx);
+                        if let Err(terr) = self.sub_types(
+                            ty,
+                            resume_ty.ty,
+                            term_location.to_locations(),
+                            ConstraintCategory::Yield,
+                        ) {
+                            span_mirbug!(
+                                self,
+                                term,
+                                "type of resume place is {:?}, but the resume type is {:?}: {:?}",
+                                resume_ty,
+                                ty,
+                                terr
+                            );
+                        }
+                    }
+                }
             }
         }
     }
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index a02304a2f8b..addb41ff5fc 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -76,6 +76,8 @@ pub struct UniversalRegions<'tcx> {
     pub unnormalized_input_tys: &'tcx [Ty<'tcx>],
 
     pub yield_ty: Option<Ty<'tcx>>,
+
+    pub resume_ty: Option<Ty<'tcx>>,
 }
 
 /// The "defining type" for this MIR. The key feature of the "defining
@@ -525,9 +527,12 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
         debug!("build: extern regions = {}..{}", first_extern_index, first_local_index);
         debug!("build: local regions  = {}..{}", first_local_index, num_universals);
 
-        let yield_ty = match defining_ty {
-            DefiningTy::Coroutine(_, args) => Some(args.as_coroutine().yield_ty()),
-            _ => None,
+        let (resume_ty, yield_ty) = match defining_ty {
+            DefiningTy::Coroutine(_, args) => {
+                let tys = args.as_coroutine();
+                (Some(tys.resume_ty()), Some(tys.yield_ty()))
+            }
+            _ => (None, None),
         };
 
         UniversalRegions {
@@ -541,6 +546,7 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
             unnormalized_output_ty: *unnormalized_output_ty,
             unnormalized_input_tys,
             yield_ty,
+            resume_ty,
         }
     }
 
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index b5801c1b0f1..0b2e63b403b 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -458,7 +458,7 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
             match expr_to_spanned_string(ecx, template_expr, msg) {
                 Ok(template_part) => template_part,
                 Err(err) => {
-                    if let Some((mut err, _)) = err {
+                    if let Some((err, _)) = err {
                         err.emit();
                     }
                     return None;
@@ -693,13 +693,14 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
         0 => {}
         1 => {
             let (sp, msg) = unused_operands.into_iter().next().unwrap();
-            let mut err = ecx.dcx().struct_span_err(sp, msg);
-            err.span_label(sp, msg);
-            err.help(format!(
-                "if this argument is intentionally unused, \
-                 consider using it in an asm comment: `\"/*{help_str} */\"`"
-            ));
-            err.emit();
+            ecx.dcx()
+                .struct_span_err(sp, msg)
+                .with_span_label(sp, msg)
+                .with_help(format!(
+                    "if this argument is intentionally unused, \
+                     consider using it in an asm comment: `\"/*{help_str} */\"`"
+                ))
+                .emit();
         }
         _ => {
             let mut err = ecx.dcx().struct_span_err(
@@ -747,7 +748,7 @@ pub(super) fn expand_asm<'cx>(
             };
             MacEager::expr(expr)
         }
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             DummyResult::any(sp)
         }
@@ -779,7 +780,7 @@ pub(super) fn expand_global_asm<'cx>(
                 DummyResult::any(sp)
             }
         }
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             DummyResult::any(sp)
         }
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 8fa3fe5b3e6..501d557f3ab 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -22,7 +22,7 @@ pub fn expand_assert<'cx>(
 ) -> Box<dyn MacResult + 'cx> {
     let Assert { cond_expr, custom_message } = match parse_assert(cx, span, tts) {
         Ok(assert) => assert,
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             return DummyResult::any(span);
         }
diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index 48be680b619..1bc2512a7b0 100644
--- a/compiler/rustc_builtin_macros/src/cfg.rs
+++ b/compiler/rustc_builtin_macros/src/cfg.rs
@@ -28,7 +28,7 @@ pub fn expand_cfg(
             );
             MacEager::expr(cx.expr_bool(sp, matches_cfg))
         }
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             DummyResult::any(sp)
         }
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index ca26b7ed827..cfa94b0e780 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -200,7 +200,7 @@ impl CfgEval<'_, '_> {
         parser.capture_cfg = true;
         match parse_annotatable_with(&mut parser) {
             Ok(a) => annotatable = a,
-            Err(mut err) => {
+            Err(err) => {
                 err.emit();
                 return Some(annotatable);
             }
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 2803ddefba2..d956c096d24 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -18,7 +18,7 @@ pub fn inject(krate: &mut ast::Crate, parse_sess: &ParseSess, attrs: &[String])
         let start_span = parser.token.span;
         let AttrItem { path, args, tokens: _ } = match parser.parse_attr_item(false) {
             Ok(ai) => ai,
-            Err(mut err) => {
+            Err(err) => {
                 err.emit();
                 continue;
             }
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index e07eb2e490b..d6dfd0efaf9 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -454,7 +454,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for EnvNotDefinedWithUserMe
             reason = "cannot translate user-provided messages"
         )]
         let mut diag = DiagnosticBuilder::new(dcx, level, self.msg_from_user.to_string());
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag
     }
 }
@@ -618,7 +618,7 @@ impl AddToDiagnostic for FormatUnusedArg {
             rustc_errors::SubdiagnosticMessage,
         ) -> rustc_errors::SubdiagnosticMessage,
     {
-        diag.set_arg("named", self.named);
+        diag.arg("named", self.named);
         let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into());
         diag.span_label(self.span, msg);
     }
@@ -803,24 +803,23 @@ pub(crate) struct AsmClobberNoReg {
 
 impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for AsmClobberNoReg {
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
-        let mut diag = DiagnosticBuilder::new(
-            dcx,
-            level,
-            crate::fluent_generated::builtin_macros_asm_clobber_no_reg,
-        );
-        diag.set_span(self.spans.clone());
         // eager translation as `span_labels` takes `AsRef<str>`
         let lbl1 = dcx.eagerly_translate_to_string(
             crate::fluent_generated::builtin_macros_asm_clobber_abi,
             [].into_iter(),
         );
-        diag.span_labels(self.clobbers, &lbl1);
         let lbl2 = dcx.eagerly_translate_to_string(
             crate::fluent_generated::builtin_macros_asm_clobber_outputs,
             [].into_iter(),
         );
-        diag.span_labels(self.spans, &lbl2);
-        diag
+        DiagnosticBuilder::new(
+            dcx,
+            level,
+            crate::fluent_generated::builtin_macros_asm_clobber_no_reg,
+        )
+        .with_span(self.spans.clone())
+        .with_span_labels(self.clobbers, &lbl1)
+        .with_span_labels(self.spans, &lbl2)
     }
 }
 
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index a668db438eb..93381b69fdc 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -99,7 +99,7 @@ fn parse_args<'a>(ecx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<
             }
 
             match p.expect(&token::Comma) {
-                Err(mut err) => {
+                Err(err) => {
                     match token::TokenKind::Comma.similar_tokens() {
                         Some(tks) if tks.contains(&p.token.kind) => {
                             // If a similar token is found, then it may be a typo. We
@@ -630,8 +630,7 @@ fn report_missing_placeholders(
         .collect::<Vec<_>>();
 
     if !placeholders.is_empty() {
-        if let Some(mut new_diag) = report_redundant_format_arguments(ecx, args, used, placeholders)
-        {
+        if let Some(new_diag) = report_redundant_format_arguments(ecx, args, used, placeholders) {
             diag.cancel();
             new_diag.emit();
             return;
@@ -976,7 +975,7 @@ fn expand_format_args_impl<'cx>(
                 MacEager::expr(DummyResult::raw_expr(sp, true))
             }
         }
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             DummyResult::any(sp)
         }
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 4fddaa8ab6c..477e5c8bec5 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -194,7 +194,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
 
                     self.dcx
                         .struct_span_err(attr.span, msg)
-                        .span_label(prev_attr.span, "previous attribute here")
+                        .with_span_label(prev_attr.span, "previous attribute here")
                         .emit();
 
                     return;
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index 30f80bb87dd..e7d7b4a7012 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -109,7 +109,7 @@ pub fn expand_include<'cx>(
     // The file will be added to the code map by the parser
     let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             return DummyResult::any(sp);
         }
@@ -146,7 +146,7 @@ pub fn expand_include<'cx>(
             let mut ret = SmallVec::new();
             loop {
                 match self.p.parse_item(ForceCollect::No) {
-                    Err(mut err) => {
+                    Err(err) => {
                         err.emit();
                         break;
                     }
@@ -155,7 +155,7 @@ pub fn expand_include<'cx>(
                         if self.p.token != token::Eof {
                             let token = pprust::token_to_string(&self.p.token);
                             let msg = format!("expected item, found `{token}`");
-                            self.p.dcx().struct_span_err(self.p.token.span, msg).emit();
+                            self.p.dcx().span_err(self.p.token.span, msg);
                         }
 
                         break;
@@ -181,7 +181,7 @@ pub fn expand_include_str(
     };
     let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             return DummyResult::any(sp);
         }
@@ -215,7 +215,7 @@ pub fn expand_include_bytes(
     };
     let file = match resolve_path(&cx.sess.parse_sess, file.as_str(), sp) {
         Ok(f) => f,
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             return DummyResult::any(sp);
         }
diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs
index 2af46f175d7..4d44e340ae1 100644
--- a/compiler/rustc_builtin_macros/src/test.rs
+++ b/compiler/rustc_builtin_macros/src/test.rs
@@ -394,11 +394,11 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>)
     let level = match item.map(|i| &i.kind) {
         // These were a warning before #92959 and need to continue being that to avoid breaking
         // stable user code (#94508).
-        Some(ast::ItemKind::MacCall(_)) => Level::Warning(None),
-        _ => Level::Error { lint: false },
+        Some(ast::ItemKind::MacCall(_)) => Level::Warning,
+        _ => Level::Error,
     };
     let mut err = DiagnosticBuilder::<()>::new(dcx, level, msg);
-    err.set_span(attr_sp);
+    err.span(attr_sp);
     if let Some(item) = item {
         err.span_label(
             item.span,
@@ -409,8 +409,8 @@ fn not_testable_error(cx: &ExtCtxt<'_>, attr_sp: Span, item: Option<&ast::Item>)
             ),
         );
     }
-    err.span_label(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions")
-        .span_suggestion(attr_sp,
+    err.with_span_label(attr_sp, "the `#[test]` macro causes a function to be run as a test and has no effect on non-functions")
+        .with_span_suggestion(attr_sp,
             "replace with conditional compilation to make the item only exist when tests are being run",
             "#[cfg(test)]",
             Applicability::MaybeIncorrect)
@@ -480,7 +480,7 @@ fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
                                 "argument must be of the form: \
                              `expected = \"error message\"`",
                             )
-                            .note(
+                            .with_note(
                                 "errors in this attribute were erroneously \
                                 allowed and will become a hard error in a \
                                 future release",
diff --git a/compiler/rustc_builtin_macros/src/type_ascribe.rs b/compiler/rustc_builtin_macros/src/type_ascribe.rs
index 72b85af1486..564797012ae 100644
--- a/compiler/rustc_builtin_macros/src/type_ascribe.rs
+++ b/compiler/rustc_builtin_macros/src/type_ascribe.rs
@@ -12,7 +12,7 @@ pub fn expand_type_ascribe(
 ) -> Box<dyn base::MacResult + 'static> {
     let (expr, ty) = match parse_ascribe(cx, tts) {
         Ok(parsed) => parsed,
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
             return DummyResult::any(span);
         }
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 a1cdf31c68a..2a7b1107ffc 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -111,6 +111,9 @@ fn start<T: Termination + 'static>(
 }
 
 static mut NUM: u8 = 6 * 7;
+
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[allow(static_mut_ref)]
 static NUM_REF: &'static u8 = unsafe { &NUM };
 
 unsafe fn zeroed<T>() -> T {
diff --git a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
index 6230ca15d6e..d1b21d0a0b6 100644
--- a/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
+++ b/compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs
@@ -68,15 +68,7 @@ impl DebugContext {
         // 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 (when the macro is
         // annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
-        let span = if tcx.should_collapse_debuginfo(span) {
-            span
-        } else {
-            // 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.
-            rustc_span::hygiene::walk_chain(span, function_span.ctxt())
-        };
-
+        let span = tcx.collapsed_debuginfo(span, function_span);
         match tcx.sess.source_map().lookup_line(span.lo()) {
             Ok(SourceFileAndLine { sf: file, line }) => {
                 let line_pos = file.lines()[line];
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index a8d8fb189e2..50d9f287e74 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -321,9 +321,10 @@ fn dep_symbol_lookup_fn(
             Linkage::NotLinked | Linkage::IncludedFromDylib => {}
             Linkage::Static => {
                 let name = crate_info.crate_name[&cnum];
-                let mut err = sess.dcx().struct_err(format!("Can't load static lib {}", name));
-                err.note("rustc_codegen_cranelift can only load dylibs in JIT mode.");
-                err.emit();
+                sess.dcx()
+                    .struct_err(format!("Can't load static lib {}", name))
+                    .note("rustc_codegen_cranelift can only load dylibs in JIT mode.")
+                    .emit();
             }
             Linkage::Dynamic => {
                 dylib_paths.push(src.dylib.as_ref().unwrap().0.clone());
diff --git a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
index 40a1ad22c0e..9827e299f2a 100644
--- a/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_gcc/example/mini_core_hello_world.rs
@@ -98,6 +98,9 @@ fn start<T: Termination + 'static>(
 }
 
 static mut NUM: u8 = 6 * 7;
+
+// FIXME: Use `SyncUnsafeCell` instead of allowing `static_mut_ref` lint
+#[allow(static_mut_ref)]
 static NUM_REF: &'static u8 = unsafe { &NUM };
 
 macro_rules! assert {
diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs
index ddd67a994c9..78e8e32b972 100644
--- a/compiler/rustc_codegen_gcc/src/asm.rs
+++ b/compiler/rustc_codegen_gcc/src/asm.rs
@@ -634,6 +634,7 @@ fn reg_to_gcc(reg: InlineAsmRegOrRegClass) -> ConstraintOrRegister {
             }
             InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
             InlineAsmRegClass::Err => unreachable!(),
         }
@@ -704,7 +705,9 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
         InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => {
             bug!("LLVM backend does not support SPIR-V")
         },
-        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::S390x(
+            S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr
+        ) => cx.type_i32(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
         InlineAsmRegClass::Err => unreachable!(),
     }
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index 1b1ed0b411c..e9283b19894 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -119,12 +119,12 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnabl
             fluent::codegen_gcc_target_feature_disable_or_enable
         );
         if let Some(span) = self.span {
-            diag.set_span(span);
+            diag.span(span);
         };
         if let Some(missing_features) = self.missing_features {
             diag.subdiagnostic(missing_features);
         }
-        diag.set_arg("features", self.features.join(", "));
+        diag.arg("features", self.features.join(", "));
         diag
     }
 }
diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs
index df917d527ce..4babe5bfb81 100644
--- a/compiler/rustc_codegen_gcc/src/gcc_util.rs
+++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs
@@ -52,7 +52,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
                 Some(c @ ('+' | '-')) => c,
                 Some(_) => {
                     if diagnostics {
-                        sess.dcx().emit_warning(UnknownCTargetFeaturePrefix { feature: s });
+                        sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s });
                     }
                     return None;
                 }
@@ -79,7 +79,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
                     else {
                         UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
                     };
-                sess.dcx().emit_warning(unknown_feature);
+                sess.dcx().emit_warn(unknown_feature);
             }
 
             if diagnostics {
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index f69f850c1d4..03f8f43ff16 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -191,7 +191,7 @@ impl CodegenBackend for GccCodegenBackend {
         #[cfg(feature="master")]
         gccjit::set_global_personality_function_name(b"rust_eh_personality\0");
         if sess.lto() == Lto::Thin {
-            sess.dcx().emit_warning(LTONotSupported {});
+            sess.dcx().emit_warn(LTONotSupported {});
         }
 
         #[cfg(not(feature="master"))]
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index 97dc401251c..0718bebb31b 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -6,7 +6,7 @@ use crate::type_::Type;
 use crate::type_of::LayoutLlvmExt;
 use crate::value::Value;
 
-use rustc_codegen_ssa::mir::operand::OperandValue;
+use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue};
 use rustc_codegen_ssa::mir::place::PlaceRef;
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::MemFlags;
@@ -253,7 +253,7 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 bx.lifetime_end(llscratch, scratch_size);
             }
         } else {
-            OperandValue::Immediate(val).store(bx, dst);
+            OperandRef::from_immediate_or_packed_pair(bx, val, self.layout).val.store(bx, dst);
         }
     }
 
diff --git a/compiler/rustc_codegen_llvm/src/allocator.rs b/compiler/rustc_codegen_llvm/src/allocator.rs
index 58b3aa438c5..ca376029735 100644
--- a/compiler/rustc_codegen_llvm/src/allocator.rs
+++ b/compiler/rustc_codegen_llvm/src/allocator.rs
@@ -134,7 +134,8 @@ fn create_wrapper_function(
             llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
         }
         if tcx.sess.must_emit_unwind_tables() {
-            let uwtable = attributes::uwtable_attr(llcx);
+            let uwtable =
+                attributes::uwtable_attr(llcx, tcx.sess.opts.unstable_opts.use_sync_unwind);
             attributes::apply_to_llfn(llfn, llvm::AttributePlace::Function, &[uwtable]);
         }
 
diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs
index 1323261ae92..a413466093b 100644
--- a/compiler/rustc_codegen_llvm/src/asm.rs
+++ b/compiler/rustc_codegen_llvm/src/asm.rs
@@ -690,6 +690,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => "w",
             InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => "e",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => "r",
+            InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg_addr) => "a",
             InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => "f",
             InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => "r",
             InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => "r",
@@ -867,7 +868,9 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
         InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_pair) => cx.type_i16(),
         InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_iw) => cx.type_i16(),
         InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg_ptr) => cx.type_i16(),
-        InlineAsmRegClass::S390x(S390xInlineAsmRegClass::reg) => cx.type_i32(),
+        InlineAsmRegClass::S390x(
+            S390xInlineAsmRegClass::reg | S390xInlineAsmRegClass::reg_addr,
+        ) => cx.type_i32(),
         InlineAsmRegClass::S390x(S390xInlineAsmRegClass::freg) => cx.type_f64(),
         InlineAsmRegClass::Msp430(Msp430InlineAsmRegClass::reg) => cx.type_i16(),
         InlineAsmRegClass::M68k(M68kInlineAsmRegClass::reg) => cx.type_i32(),
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index b3fa7b7cd44..0a7ea599431 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -95,11 +95,12 @@ pub fn sanitize_attrs<'ll>(
 
 /// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function.
 #[inline]
-pub fn uwtable_attr(llcx: &llvm::Context) -> &Attribute {
+pub fn uwtable_attr(llcx: &llvm::Context, use_sync_unwind: Option<bool>) -> &Attribute {
     // NOTE: We should determine if we even need async unwind tables, as they
     // take have more overhead and if we can use sync unwind tables we
     // probably should.
-    llvm::CreateUWTableAttr(llcx, true)
+    let async_unwind = !use_sync_unwind.unwrap_or(false);
+    llvm::CreateUWTableAttr(llcx, async_unwind)
 }
 
 pub fn frame_pointer_type_attr<'ll>(cx: &CodegenCx<'ll, '_>) -> Option<&'ll Attribute> {
@@ -333,7 +334,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
     // You can also find more info on why Windows always requires uwtables here:
     //      https://bugzilla.mozilla.org/show_bug.cgi?id=1302078
     if cx.sess().must_emit_unwind_tables() {
-        to_add.push(uwtable_attr(cx.llcx));
+        to_add.push(uwtable_attr(cx.llcx, cx.sess().opts.unstable_opts.use_sync_unwind));
     }
 
     if cx.sess().opts.unstable_opts.profile_sample_use.is_some() {
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index c607533a08e..a912ef9e755 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -245,12 +245,12 @@ pub fn target_machine_factory(
     match sess.opts.debuginfo_compression {
         rustc_session::config::DebugInfoCompression::Zlib => {
             if !unsafe { LLVMRustLLVMHasZlibCompressionForDebugSymbols() } {
-                sess.dcx().emit_warning(UnknownCompression { algorithm: "zlib" });
+                sess.dcx().emit_warn(UnknownCompression { algorithm: "zlib" });
             }
         }
         rustc_session::config::DebugInfoCompression::Zstd => {
             if !unsafe { LLVMRustLLVMHasZstdCompressionForDebugSymbols() } {
-                sess.dcx().emit_warning(UnknownCompression { algorithm: "zstd" });
+                sess.dcx().emit_warn(UnknownCompression { algorithm: "zstd" });
             }
         }
         rustc_session::config::DebugInfoCompression::None => {}
@@ -416,8 +416,8 @@ fn report_inline_asm(
         cookie = 0;
     }
     let level = match level {
-        llvm::DiagnosticLevel::Error => Level::Error { lint: false },
-        llvm::DiagnosticLevel::Warning => Level::Warning(None),
+        llvm::DiagnosticLevel::Error => Level::Error,
+        llvm::DiagnosticLevel::Warning => Level::Warning,
         llvm::DiagnosticLevel::Note | llvm::DiagnosticLevel::Remark => Level::Note,
     };
     cgcx.diag_emitter.inline_asm_error(cookie as u32, msg, level, source);
@@ -457,7 +457,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
                 llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
             })
             .expect("non-UTF8 diagnostic");
-            dcx.emit_warning(FromLlvmDiag { message });
+            dcx.emit_warn(FromLlvmDiag { message });
         }
         llvm::diagnostic::Unsupported(diagnostic_ref) => {
             let message = llvm::build_string(|s| {
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 4e5fe290bb1..7ed27b33dce 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -558,10 +558,17 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
             OperandValue::Immediate(self.to_immediate(llval, place.layout))
         } else if let abi::Abi::ScalarPair(a, b) = place.layout.abi {
             let b_offset = a.size(self).align_to(b.align(self).abi);
-            let pair_ty = place.layout.llvm_type(self);
 
             let mut load = |i, scalar: abi::Scalar, layout, align, offset| {
-                let llptr = self.struct_gep(pair_ty, place.llval, i as u64);
+                let llptr = if i == 0 {
+                    place.llval
+                } else {
+                    self.inbounds_gep(
+                        self.type_i8(),
+                        place.llval,
+                        &[self.const_usize(b_offset.bytes())],
+                    )
+                };
                 let llty = place.layout.scalar_pair_element_llvm_type(self, i, false);
                 let load = self.load(llty, llptr, align);
                 scalar_load_metadata(self, load, scalar, layout, offset);
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 8db97d577ca..697ce602298 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -105,10 +105,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ParseTargetMachineConfig<'_
         let (message, _) = diag.messages().first().expect("`LlvmError` with no message");
         let message = dcx.eagerly_translate_to_string(message.clone(), diag.args());
 
-        let mut diag =
-            DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config);
-        diag.set_arg("error", message);
-        diag
+        DiagnosticBuilder::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config)
+            .with_arg("error", message)
     }
 }
 
@@ -130,12 +128,12 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetFeatureDisableOrEnabl
             fluent::codegen_llvm_target_feature_disable_or_enable,
         );
         if let Some(span) = self.span {
-            diag.set_span(span);
+            diag.span(span);
         };
         if let Some(missing_features) = self.missing_features {
             diag.subdiagnostic(missing_features);
         }
-        diag.set_arg("features", self.features.join(", "));
+        diag.arg("features", self.features.join(", "));
         diag
     }
 }
@@ -204,10 +202,10 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for WithLlvmError<'_> {
             PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err,
             ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err,
         };
-        let mut diag = self.0.into_diagnostic(dcx, level);
-        diag.set_primary_message(msg_with_llvm_err);
-        diag.set_arg("llvm_err", self.1);
-        diag
+        self.0
+            .into_diagnostic(dcx, level)
+            .with_primary_message(msg_with_llvm_err)
+            .with_arg("llvm_err", self.1)
     }
 }
 
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 6043a8ebded..a0f9d5cf7cd 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -179,7 +179,10 @@ impl<'ll, 'tcx> IntrinsicCallMethods<'tcx> for Builder<'_, 'll, 'tcx> {
                 unsafe {
                     llvm::LLVMSetAlignment(load, align);
                 }
-                self.to_immediate(load, self.layout_of(tp_ty))
+                if !result.layout.is_zst() {
+                    self.store(load, result.llval, result.align);
+                }
+                return;
             }
             sym::volatile_store => {
                 let dst = args[0].deref(self.cx());
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 03b79a143cc..99f4488ac0f 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -529,7 +529,7 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
                 Some(c @ ('+' | '-')) => c,
                 Some(_) => {
                     if diagnostics {
-                        sess.dcx().emit_warning(UnknownCTargetFeaturePrefix { feature: s });
+                        sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s });
                     }
                     return None;
                 }
@@ -557,12 +557,12 @@ pub(crate) fn global_llvm_features(sess: &Session, diagnostics: bool) -> Vec<Str
                     } else {
                         UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
                     };
-                    sess.dcx().emit_warning(unknown_feature);
+                    sess.dcx().emit_warn(unknown_feature);
                 } else if feature_state
                     .is_some_and(|(_name, feature_gate)| !feature_gate.is_stable())
                 {
                     // An unstable feature. Warn about using it.
-                    sess.dcx().emit_warning(UnstableCTargetFeature { feature });
+                    sess.dcx().emit_warn(UnstableCTargetFeature { feature });
                 }
             }
 
diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs
index 57b46382c96..e88f4217c9d 100644
--- a/compiler/rustc_codegen_llvm/src/type_of.rs
+++ b/compiler/rustc_codegen_llvm/src/type_of.rs
@@ -26,16 +26,7 @@ fn uncached_llvm_type<'a, 'tcx>(
             let element = layout.scalar_llvm_type_at(cx, element);
             return cx.type_vector(element, count);
         }
-        Abi::ScalarPair(..) => {
-            return cx.type_struct(
-                &[
-                    layout.scalar_pair_element_llvm_type(cx, 0, false),
-                    layout.scalar_pair_element_llvm_type(cx, 1, false),
-                ],
-                false,
-            );
-        }
-        Abi::Uninhabited | Abi::Aggregate { .. } => {}
+        Abi::Uninhabited | Abi::Aggregate { .. } | Abi::ScalarPair(..) => {}
     }
 
     let name = match layout.ty.kind() {
@@ -275,11 +266,25 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
     }
 
     fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> &'a Type {
-        if let Abi::Scalar(scalar) = self.abi {
-            if scalar.is_bool() {
-                return cx.type_i1();
+        match self.abi {
+            Abi::Scalar(scalar) => {
+                if scalar.is_bool() {
+                    return cx.type_i1();
+                }
             }
-        }
+            Abi::ScalarPair(..) => {
+                // An immediate pair always contains just the two elements, without any padding
+                // filler, as it should never be stored to memory.
+                return cx.type_struct(
+                    &[
+                        self.scalar_pair_element_llvm_type(cx, 0, true),
+                        self.scalar_pair_element_llvm_type(cx, 1, true),
+                    ],
+                    false,
+                );
+            }
+            _ => {}
+        };
         self.llvm_type(cx)
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 4ff497f2fdd..70fda982b01 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1016,7 +1016,7 @@ fn link_natively<'a>(
                     if !prog.status.success() {
                         let mut output = prog.stderr.clone();
                         output.extend_from_slice(&prog.stdout);
-                        sess.dcx().emit_warning(errors::ProcessingDymutilFailed {
+                        sess.dcx().emit_warn(errors::ProcessingDymutilFailed {
                             status: prog.status,
                             output: escape_string(&output),
                         });
@@ -1091,7 +1091,7 @@ fn strip_symbols_with_external_utility<'a>(
             if !prog.status.success() {
                 let mut output = prog.stderr.clone();
                 output.extend_from_slice(&prog.stdout);
-                sess.dcx().emit_warning(errors::StrippingDebugInfoFailed {
+                sess.dcx().emit_warn(errors::StrippingDebugInfoFailed {
                     util,
                     status: prog.status,
                     output: escape_string(&output),
@@ -1186,15 +1186,22 @@ mod win {
     }
 }
 
-fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut dyn Linker) {
-    // On macOS the runtimes are distributed as dylibs which should be linked to
-    // both executables and dynamic shared objects. Everywhere else the runtimes
-    // are currently distributed as static libraries which should be linked to
-    // executables only.
+fn add_sanitizer_libraries(
+    sess: &Session,
+    flavor: LinkerFlavor,
+    crate_type: CrateType,
+    linker: &mut dyn Linker,
+) {
+    // On macOS and Windows using MSVC the runtimes are distributed as dylibs
+    // which should be linked to both executables and dynamic libraries.
+    // Everywhere else the runtimes are currently distributed as static
+    // libraries which should be linked to executables only.
     let needs_runtime = !sess.target.is_like_android
         && match crate_type {
             CrateType::Executable => true,
-            CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => sess.target.is_like_osx,
+            CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro => {
+                sess.target.is_like_osx || sess.target.is_like_msvc
+            }
             CrateType::Rlib | CrateType::Staticlib => false,
         };
 
@@ -1204,26 +1211,31 @@ fn add_sanitizer_libraries(sess: &Session, crate_type: CrateType, linker: &mut d
 
     let sanitizer = sess.opts.unstable_opts.sanitizer;
     if sanitizer.contains(SanitizerSet::ADDRESS) {
-        link_sanitizer_runtime(sess, linker, "asan");
+        link_sanitizer_runtime(sess, flavor, linker, "asan");
     }
     if sanitizer.contains(SanitizerSet::LEAK) {
-        link_sanitizer_runtime(sess, linker, "lsan");
+        link_sanitizer_runtime(sess, flavor, linker, "lsan");
     }
     if sanitizer.contains(SanitizerSet::MEMORY) {
-        link_sanitizer_runtime(sess, linker, "msan");
+        link_sanitizer_runtime(sess, flavor, linker, "msan");
     }
     if sanitizer.contains(SanitizerSet::THREAD) {
-        link_sanitizer_runtime(sess, linker, "tsan");
+        link_sanitizer_runtime(sess, flavor, linker, "tsan");
     }
     if sanitizer.contains(SanitizerSet::HWADDRESS) {
-        link_sanitizer_runtime(sess, linker, "hwasan");
+        link_sanitizer_runtime(sess, flavor, linker, "hwasan");
     }
     if sanitizer.contains(SanitizerSet::SAFESTACK) {
-        link_sanitizer_runtime(sess, linker, "safestack");
+        link_sanitizer_runtime(sess, flavor, linker, "safestack");
     }
 }
 
-fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
+fn link_sanitizer_runtime(
+    sess: &Session,
+    flavor: LinkerFlavor,
+    linker: &mut dyn Linker,
+    name: &str,
+) {
     fn find_sanitizer_runtime(sess: &Session, filename: &str) -> PathBuf {
         let session_tlib =
             filesearch::make_target_lib_path(&sess.sysroot, sess.opts.target_triple.triple());
@@ -1254,6 +1266,10 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
         let rpath = path.to_str().expect("non-utf8 component in path");
         linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
         linker.link_dylib(&filename, false, true);
+    } else if sess.target.is_like_msvc && flavor == LinkerFlavor::Msvc(Lld::No) && name == "asan" {
+        // MSVC provides the `/INFERASANLIBS` argument to automatically find the
+        // compatible ASAN library.
+        linker.arg("/INFERASANLIBS");
     } else {
         let filename = format!("librustc{channel}_rt.{name}.a");
         let path = find_sanitizer_runtime(sess, &filename).join(&filename);
@@ -2076,7 +2092,7 @@ fn linker_with_args<'a>(
     );
 
     // Sanitizer libraries.
-    add_sanitizer_libraries(sess, crate_type, cmd);
+    add_sanitizer_libraries(sess, flavor, crate_type, cmd);
 
     // Object code from the current crate.
     // Take careful note of the ordering of the arguments we pass to the linker
@@ -2390,7 +2406,7 @@ fn collect_natvis_visualizers(
                 visualizer_paths.push(visualizer_out_file);
             }
             Err(error) => {
-                sess.dcx().emit_warning(errors::UnableToWriteDebuggerVisualizer {
+                sess.dcx().emit_warn(errors::UnableToWriteDebuggerVisualizer {
                     path: visualizer_out_file,
                     error,
                 });
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 695aeb0b2fb..90f5027c264 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -446,11 +446,11 @@ impl<'a> Linker for GccLinker<'a> {
                 // FIXME(81490): ld64 doesn't support these flags but macOS 11
                 // has -needed-l{} / -needed_library {}
                 // but we have no way to detect that here.
-                self.sess.dcx().emit_warning(errors::Ld64UnimplementedModifier);
+                self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
             } else if self.is_gnu && !self.sess.target.is_like_windows {
                 self.linker_arg("--no-as-needed");
             } else {
-                self.sess.dcx().emit_warning(errors::LinkerUnsupportedModifier);
+                self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
             }
         }
         self.hint_dynamic();
@@ -504,7 +504,7 @@ impl<'a> Linker for GccLinker<'a> {
             // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework
             // flag but we have no way to detect that here.
             // self.cmd.arg("-needed_framework").arg(framework);
-            self.sess.dcx().emit_warning(errors::Ld64UnimplementedModifier);
+            self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
         }
         self.cmd.arg("-framework").arg(framework);
     }
@@ -950,7 +950,7 @@ impl<'a> Linker for MsvcLinker<'a> {
                                 }
                             }
                             Err(error) => {
-                                self.sess.dcx().emit_warning(errors::NoNatvisDirectory { error });
+                                self.sess.dcx().emit_warn(errors::NoNatvisDirectory { error });
                             }
                         }
                     }
@@ -1501,7 +1501,7 @@ impl<'a> Linker for L4Bender<'a> {
 
     fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
         // ToDo, not implemented, copy from GCC
-        self.sess.dcx().emit_warning(errors::L4BenderExportingSymbolsUnimplemented);
+        self.sess.dcx().emit_warn(errors::L4BenderExportingSymbolsUnimplemented);
         return;
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 54b523cb6bd..94841ab7b33 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -3,7 +3,7 @@ use crate::base::allocator_kind_for_codegen;
 use std::collections::hash_map::Entry::*;
 
 use rustc_ast::expand::allocator::{ALLOCATOR_METHODS, NO_ALLOC_SHIM_IS_UNSTABLE};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordMap;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -393,10 +393,10 @@ fn exported_symbols_provider_local(
 fn upstream_monomorphizations_provider(
     tcx: TyCtxt<'_>,
     (): (),
-) -> DefIdMap<FxHashMap<GenericArgsRef<'_>, CrateNum>> {
+) -> DefIdMap<UnordMap<GenericArgsRef<'_>, CrateNum>> {
     let cnums = tcx.crates(());
 
-    let mut instances: DefIdMap<FxHashMap<_, _>> = Default::default();
+    let mut instances: DefIdMap<UnordMap<_, _>> = Default::default();
 
     let drop_in_place_fn_def_id = tcx.lang_items().drop_in_place_fn();
 
@@ -445,7 +445,7 @@ fn upstream_monomorphizations_provider(
 fn upstream_monomorphizations_for_provider(
     tcx: TyCtxt<'_>,
     def_id: DefId,
-) -> Option<&FxHashMap<GenericArgsRef<'_>, CrateNum>> {
+) -> Option<&UnordMap<GenericArgsRef<'_>, CrateNum>> {
     debug_assert!(!def_id.is_local());
     tcx.upstream_monomorphizations(()).get(&def_id)
 }
@@ -656,7 +656,7 @@ fn maybe_emutls_symbol_name<'tcx>(
     }
 }
 
-fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
+fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<String> {
     // Build up a map from DefId to a `NativeLib` structure, where
     // `NativeLib` internally contains information about
     // `#[link(wasm_import_module = "...")]` for example.
@@ -665,9 +665,9 @@ fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, S
     let def_id_to_native_lib = native_libs
         .iter()
         .filter_map(|lib| lib.foreign_module.map(|id| (id, lib)))
-        .collect::<FxHashMap<_, _>>();
+        .collect::<DefIdMap<_>>();
 
-    let mut ret = FxHashMap::default();
+    let mut ret = DefIdMap::default();
     for (def_id, lib) in tcx.foreign_modules(cnum).iter() {
         let module = def_id_to_native_lib.get(def_id).and_then(|s| s.wasm_import_module());
         let Some(module) = module else { continue };
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 5a8db7bbf2d..8e835039970 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -573,11 +573,11 @@ fn produce_final_output_artifacts(
             if crate_output.outputs.contains_key(&output_type) {
                 // 2) Multiple codegen units, with `--emit foo=some_name`. We have
                 //    no good solution for this case, so warn the user.
-                sess.dcx().emit_warning(errors::IgnoringEmitPath { extension });
+                sess.dcx().emit_warn(errors::IgnoringEmitPath { extension });
             } else if crate_output.single_output_file.is_some() {
                 // 3) Multiple codegen units, with `-o some_name`. We have
                 //    no good solution for this case, so warn the user.
-                sess.dcx().emit_warning(errors::IgnoringOutput { extension });
+                sess.dcx().emit_warn(errors::IgnoringOutput { extension });
             } else {
                 // 4) Multiple codegen units, but no explicit name. We
                 //    just leave the `foo.0.x` files in place.
@@ -1847,20 +1847,15 @@ impl SharedEmitterMain {
                     dcx.emit_diagnostic(d);
                 }
                 Ok(SharedEmitterMessage::InlineAsmError(cookie, msg, level, source)) => {
-                    let err_level = match level {
-                        Level::Error { lint: false } => rustc_errors::Level::Error { lint: false },
-                        Level::Warning(_) => rustc_errors::Level::Warning(None),
-                        Level::Note => rustc_errors::Level::Note,
-                        _ => bug!("Invalid inline asm diagnostic level"),
-                    };
+                    assert!(matches!(level, Level::Error | Level::Warning | Level::Note));
                     let msg = msg.strip_prefix("error: ").unwrap_or(&msg).to_string();
-                    let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), err_level, msg);
+                    let mut err = DiagnosticBuilder::<()>::new(sess.dcx(), level, msg);
 
                     // If the cookie is 0 then we don't have span information.
                     if cookie != 0 {
                         let pos = BytePos::from_u32(cookie);
                         let span = Span::with_root_ctxt(pos, pos);
-                        err.set_span(span);
+                        err.span(span);
                     };
 
                     // Point to the generated assembly if it is available.
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index 63fd7b42f7b..36d7234a6ea 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,6 +1,6 @@
 use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
@@ -216,7 +216,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 if let Some(fn_sig) = fn_sig()
                     && !matches!(fn_sig.skip_binder().abi(), abi::Abi::C { .. })
                 {
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         attr.span,
                         E0776,
@@ -225,7 +225,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     .emit();
                 }
                 if !tcx.sess.target.llvm_target.contains("thumbv8m") {
-                    struct_span_err!(tcx.dcx(), attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
+                    struct_span_code_err!(tcx.dcx(), attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
                     .emit();
                 }
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY
@@ -238,7 +238,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     && let Some(fn_sig) = fn_sig()
                     && fn_sig.skip_binder().abi() != abi::Abi::Rust
                 {
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         attr.span,
                         E0737,
@@ -265,7 +265,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     if s.as_str().contains('\0') {
                         // `#[export_name = ...]` will be converted to a null-terminated string,
                         // so it may not contain any null characters.
-                        struct_span_err!(
+                        struct_span_code_err!(
                             tcx.dcx(),
                             attr.span,
                             E0648,
@@ -303,14 +303,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                         // This exception needs to be kept in sync with allowing
                         // `#[target_feature]` on `main` and `start`.
                     } else if !tcx.features().target_feature_11 {
-                        let mut err = feature_err(
+                        feature_err(
                             &tcx.sess.parse_sess,
                             sym::target_feature_11,
                             attr.span,
                             "`#[target_feature(..)]` can only be applied to `unsafe` functions",
-                        );
-                        err.span_label(tcx.def_span(did), "not an `unsafe` function");
-                        err.emit();
+                        )
+                        .with_span_label(tcx.def_span(did), "not an `unsafe` function")
+                        .emit();
                     } else {
                         check_target_feature_trait_unsafe(tcx, did, attr.span);
                     }
@@ -385,7 +385,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                             match segments.as_slice() {
                                 [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
                                     if !tcx.sess.target.has_thumb_interworking {
-                                        struct_span_err!(
+                                        struct_span_code_err!(
                                             tcx.dcx(),
                                             attr.span,
                                             E0779,
@@ -402,7 +402,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                                     }
                                 }
                                 _ => {
-                                    struct_span_err!(
+                                    struct_span_code_err!(
                                         tcx.dcx(),
                                         attr.span,
                                         E0779,
@@ -414,7 +414,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                             }
                         }
                         [] => {
-                            struct_span_err!(
+                            struct_span_code_err!(
                                 tcx.dcx(),
                                 attr.span,
                                 E0778,
@@ -424,7 +424,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                             None
                         }
                         _ => {
-                            struct_span_err!(
+                            struct_span_code_err!(
                                 tcx.dcx(),
                                 attr.span,
                                 E0779,
@@ -442,7 +442,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                 {
                     rustc_attr::parse_alignment(&literal.kind)
                         .map_err(|msg| {
-                            struct_span_err!(
+                            struct_span_code_err!(
                                 tcx.dcx(),
                                 attr.span,
                                 E0589,
@@ -469,15 +469,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
             Some(MetaItemKind::List(ref items)) => {
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
-                    struct_span_err!(tcx.dcx(), attr.span, E0534, "expected one argument").emit();
+                    struct_span_code_err!(tcx.dcx(), attr.span, E0534, "expected one argument")
+                        .emit();
                     InlineAttr::None
                 } else if list_contains_name(items, sym::always) {
                     InlineAttr::Always
                 } else if list_contains_name(items, sym::never) {
                     InlineAttr::Never
                 } else {
-                    struct_span_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
-                        .help("valid inline arguments are `always` and `never`")
+                    struct_span_code_err!(tcx.dcx(), items[0].span(), E0535, "invalid argument")
+                        .with_help("valid inline arguments are `always` and `never`")
                         .emit();
 
                     InlineAttr::None
@@ -492,7 +493,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
         if !attr.has_name(sym::optimize) {
             return ia;
         }
-        let err = |sp, s| struct_span_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
+        let err = |sp, s| struct_span_code_err!(tcx.dcx(), sp, E0722, "{}", s).emit();
         match attr.meta_kind() {
             Some(MetaItemKind::Word) => {
                 err(attr.span, "expected one argument");
@@ -662,7 +663,7 @@ fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
             let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
             tcx.dcx()
                 .struct_span_err(attr.span, msg)
-                .note("the value may not exceed `u16::MAX`")
+                .with_note("the value may not exceed `u16::MAX`")
                 .emit();
             None
         }
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 2b628d2aa69..f90e1906caf 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -212,192 +212,123 @@ pub struct ThorinErrorWrapper(pub thorin::Error);
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for ThorinErrorWrapper {
     fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let build = |msg| DiagnosticBuilder::new(dcx, level, msg);
-        let mut diag;
         match self.0 {
-            thorin::Error::ReadInput(_) => {
-                diag = build(fluent::codegen_ssa_thorin_read_input_failure);
-                diag
-            }
+            thorin::Error::ReadInput(_) => build(fluent::codegen_ssa_thorin_read_input_failure),
             thorin::Error::ParseFileKind(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_input_file_kind);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_input_file_kind)
             }
             thorin::Error::ParseObjectFile(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_input_object_file);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_input_object_file)
             }
             thorin::Error::ParseArchiveFile(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_input_archive_file);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_input_archive_file)
             }
             thorin::Error::ParseArchiveMember(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_archive_member);
-                diag
-            }
-            thorin::Error::InvalidInputKind => {
-                diag = build(fluent::codegen_ssa_thorin_invalid_input_kind);
-                diag
-            }
-            thorin::Error::DecompressData(_) => {
-                diag = build(fluent::codegen_ssa_thorin_decompress_data);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_archive_member)
             }
+            thorin::Error::InvalidInputKind => build(fluent::codegen_ssa_thorin_invalid_input_kind),
+            thorin::Error::DecompressData(_) => build(fluent::codegen_ssa_thorin_decompress_data),
             thorin::Error::NamelessSection(_, offset) => {
-                diag = build(fluent::codegen_ssa_thorin_section_without_name);
-                diag.set_arg("offset", format!("0x{offset:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_section_without_name)
+                    .with_arg("offset", format!("0x{offset:08x}"))
             }
             thorin::Error::RelocationWithInvalidSymbol(section, offset) => {
-                diag = build(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol);
-                diag.set_arg("section", section);
-                diag.set_arg("offset", format!("0x{offset:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_relocation_with_invalid_symbol)
+                    .with_arg("section", section)
+                    .with_arg("offset", format!("0x{offset:08x}"))
             }
             thorin::Error::MultipleRelocations(section, offset) => {
-                diag = build(fluent::codegen_ssa_thorin_multiple_relocations);
-                diag.set_arg("section", section);
-                diag.set_arg("offset", format!("0x{offset:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_multiple_relocations)
+                    .with_arg("section", section)
+                    .with_arg("offset", format!("0x{offset:08x}"))
             }
             thorin::Error::UnsupportedRelocation(section, offset) => {
-                diag = build(fluent::codegen_ssa_thorin_unsupported_relocation);
-                diag.set_arg("section", section);
-                diag.set_arg("offset", format!("0x{offset:08x}"));
-                diag
-            }
-            thorin::Error::MissingDwoName(id) => {
-                diag = build(fluent::codegen_ssa_thorin_missing_dwo_name);
-                diag.set_arg("id", format!("0x{id:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_unsupported_relocation)
+                    .with_arg("section", section)
+                    .with_arg("offset", format!("0x{offset:08x}"))
             }
+            thorin::Error::MissingDwoName(id) => build(fluent::codegen_ssa_thorin_missing_dwo_name)
+                .with_arg("id", format!("0x{id:08x}")),
             thorin::Error::NoCompilationUnits => {
-                diag = build(fluent::codegen_ssa_thorin_no_compilation_units);
-                diag
-            }
-            thorin::Error::NoDie => {
-                diag = build(fluent::codegen_ssa_thorin_no_die);
-                diag
+                build(fluent::codegen_ssa_thorin_no_compilation_units)
             }
+            thorin::Error::NoDie => build(fluent::codegen_ssa_thorin_no_die),
             thorin::Error::TopLevelDieNotUnit => {
-                diag = build(fluent::codegen_ssa_thorin_top_level_die_not_unit);
-                diag
+                build(fluent::codegen_ssa_thorin_top_level_die_not_unit)
             }
             thorin::Error::MissingRequiredSection(section) => {
-                diag = build(fluent::codegen_ssa_thorin_missing_required_section);
-                diag.set_arg("section", section);
-                diag
+                build(fluent::codegen_ssa_thorin_missing_required_section)
+                    .with_arg("section", section)
             }
             thorin::Error::ParseUnitAbbreviations(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_unit_abbreviations);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_unit_abbreviations)
             }
             thorin::Error::ParseUnitAttribute(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_unit_attribute);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_unit_attribute)
             }
             thorin::Error::ParseUnitHeader(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_unit_header);
-                diag
-            }
-            thorin::Error::ParseUnit(_) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_unit);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_unit_header)
             }
+            thorin::Error::ParseUnit(_) => build(fluent::codegen_ssa_thorin_parse_unit),
             thorin::Error::IncompatibleIndexVersion(section, format, actual) => {
-                diag = build(fluent::codegen_ssa_thorin_incompatible_index_version);
-                diag.set_arg("section", section);
-                diag.set_arg("actual", actual);
-                diag.set_arg("format", format);
-                diag
+                build(fluent::codegen_ssa_thorin_incompatible_index_version)
+                    .with_arg("section", section)
+                    .with_arg("actual", actual)
+                    .with_arg("format", format)
             }
             thorin::Error::OffsetAtIndex(_, index) => {
-                diag = build(fluent::codegen_ssa_thorin_offset_at_index);
-                diag.set_arg("index", index);
-                diag
+                build(fluent::codegen_ssa_thorin_offset_at_index).with_arg("index", index)
             }
             thorin::Error::StrAtOffset(_, offset) => {
-                diag = build(fluent::codegen_ssa_thorin_str_at_offset);
-                diag.set_arg("offset", format!("0x{offset:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_str_at_offset)
+                    .with_arg("offset", format!("0x{offset:08x}"))
             }
             thorin::Error::ParseIndex(_, section) => {
-                diag = build(fluent::codegen_ssa_thorin_parse_index);
-                diag.set_arg("section", section);
-                diag
+                build(fluent::codegen_ssa_thorin_parse_index).with_arg("section", section)
             }
             thorin::Error::UnitNotInIndex(unit) => {
-                diag = build(fluent::codegen_ssa_thorin_unit_not_in_index);
-                diag.set_arg("unit", format!("0x{unit:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_unit_not_in_index)
+                    .with_arg("unit", format!("0x{unit:08x}"))
             }
             thorin::Error::RowNotInIndex(_, row) => {
-                diag = build(fluent::codegen_ssa_thorin_row_not_in_index);
-                diag.set_arg("row", row);
-                diag
-            }
-            thorin::Error::SectionNotInRow => {
-                diag = build(fluent::codegen_ssa_thorin_section_not_in_row);
-                diag
-            }
-            thorin::Error::EmptyUnit(unit) => {
-                diag = build(fluent::codegen_ssa_thorin_empty_unit);
-                diag.set_arg("unit", format!("0x{unit:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_row_not_in_index).with_arg("row", row)
             }
+            thorin::Error::SectionNotInRow => build(fluent::codegen_ssa_thorin_section_not_in_row),
+            thorin::Error::EmptyUnit(unit) => build(fluent::codegen_ssa_thorin_empty_unit)
+                .with_arg("unit", format!("0x{unit:08x}")),
             thorin::Error::MultipleDebugInfoSection => {
-                diag = build(fluent::codegen_ssa_thorin_multiple_debug_info_section);
-                diag
+                build(fluent::codegen_ssa_thorin_multiple_debug_info_section)
             }
             thorin::Error::MultipleDebugTypesSection => {
-                diag = build(fluent::codegen_ssa_thorin_multiple_debug_types_section);
-                diag
-            }
-            thorin::Error::NotSplitUnit => {
-                diag = build(fluent::codegen_ssa_thorin_not_split_unit);
-                diag
-            }
-            thorin::Error::DuplicateUnit(unit) => {
-                diag = build(fluent::codegen_ssa_thorin_duplicate_unit);
-                diag.set_arg("unit", format!("0x{unit:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_multiple_debug_types_section)
             }
+            thorin::Error::NotSplitUnit => build(fluent::codegen_ssa_thorin_not_split_unit),
+            thorin::Error::DuplicateUnit(unit) => build(fluent::codegen_ssa_thorin_duplicate_unit)
+                .with_arg("unit", format!("0x{unit:08x}")),
             thorin::Error::MissingReferencedUnit(unit) => {
-                diag = build(fluent::codegen_ssa_thorin_missing_referenced_unit);
-                diag.set_arg("unit", format!("0x{unit:08x}"));
-                diag
+                build(fluent::codegen_ssa_thorin_missing_referenced_unit)
+                    .with_arg("unit", format!("0x{unit:08x}"))
             }
             thorin::Error::NoOutputObjectCreated => {
-                diag = build(fluent::codegen_ssa_thorin_not_output_object_created);
-                diag
+                build(fluent::codegen_ssa_thorin_not_output_object_created)
             }
             thorin::Error::MixedInputEncodings => {
-                diag = build(fluent::codegen_ssa_thorin_mixed_input_encodings);
-                diag
+                build(fluent::codegen_ssa_thorin_mixed_input_encodings)
             }
             thorin::Error::Io(e) => {
-                diag = build(fluent::codegen_ssa_thorin_io);
-                diag.set_arg("error", format!("{e}"));
-                diag
+                build(fluent::codegen_ssa_thorin_io).with_arg("error", format!("{e}"))
             }
             thorin::Error::ObjectRead(e) => {
-                diag = build(fluent::codegen_ssa_thorin_object_read);
-                diag.set_arg("error", format!("{e}"));
-                diag
+                build(fluent::codegen_ssa_thorin_object_read).with_arg("error", format!("{e}"))
             }
             thorin::Error::ObjectWrite(e) => {
-                diag = build(fluent::codegen_ssa_thorin_object_write);
-                diag.set_arg("error", format!("{e}"));
-                diag
+                build(fluent::codegen_ssa_thorin_object_write).with_arg("error", format!("{e}"))
             }
             thorin::Error::GimliRead(e) => {
-                diag = build(fluent::codegen_ssa_thorin_gimli_read);
-                diag.set_arg("error", format!("{e}"));
-                diag
+                build(fluent::codegen_ssa_thorin_gimli_read).with_arg("error", format!("{e}"))
             }
             thorin::Error::GimliWrite(e) => {
-                diag = build(fluent::codegen_ssa_thorin_gimli_write);
-                diag.set_arg("error", format!("{e}"));
-                diag
+                build(fluent::codegen_ssa_thorin_gimli_write).with_arg("error", format!("{e}"))
             }
             _ => unimplemented!("Untranslated thorin error"),
         }
@@ -414,8 +345,8 @@ pub struct LinkingFailed<'a> {
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for LinkingFailed<'_> {
     fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::codegen_ssa_linking_failed);
-        diag.set_arg("linker_path", format!("{}", self.linker_path.display()));
-        diag.set_arg("exit_status", format!("{}", self.exit_status));
+        diag.arg("linker_path", format!("{}", self.linker_path.display()));
+        diag.arg("exit_status", format!("{}", self.exit_status));
 
         let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
 
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 14915e816ee..48f3f4f2522 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -228,21 +228,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
     /// 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 (when the macro is
     /// annotated with `#[collapse_debuginfo]` or when `-Zdebug-macros` is provided).
-    fn adjust_span_for_debugging(&self, mut span: Span) -> Span {
+    fn adjust_span_for_debugging(&self, span: Span) -> Span {
         // Bail out if debug info emission is not enabled.
         if self.debug_context.is_none() {
             return span;
         }
-
-        if self.cx.tcx().should_collapse_debuginfo(span) {
-            // 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.
-            // Use span of the outermost expansion site, while keeping the original lexical scope.
-            span = rustc_span::hygiene::walk_chain(span, self.mir.span.ctxt());
-        }
-
-        span
+        // 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.
+        // Use span of the outermost expansion site, while keeping the original lexical scope.
+        self.cx.tcx().collapsed_debuginfo(span, self.mir.span)
     }
 
     fn spill_operand_to_stack(
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 794cbd315b7..6f6f010422f 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -231,14 +231,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
         bx: &mut Bx,
     ) -> V {
         if let OperandValue::Pair(a, b) = self.val {
-            let llty = bx.cx().backend_type(self.layout);
+            let llty = bx.cx().immediate_backend_type(self.layout);
             debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty);
             // Reconstruct the immediate aggregate.
             let mut llpair = bx.cx().const_poison(llty);
-            let imm_a = bx.from_immediate(a);
-            let imm_b = bx.from_immediate(b);
-            llpair = bx.insert_value(llpair, imm_a, 0);
-            llpair = bx.insert_value(llpair, imm_b, 1);
+            llpair = bx.insert_value(llpair, a, 0);
+            llpair = bx.insert_value(llpair, b, 1);
             llpair
         } else {
             self.immediate()
@@ -251,14 +249,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
         llval: V,
         layout: TyAndLayout<'tcx>,
     ) -> Self {
-        let val = if let Abi::ScalarPair(a, b) = layout.abi {
+        let val = if let Abi::ScalarPair(..) = layout.abi {
             debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout);
 
             // Deconstruct the immediate aggregate.
             let a_llval = bx.extract_value(llval, 0);
-            let a_llval = bx.to_immediate_scalar(a_llval, a);
             let b_llval = bx.extract_value(llval, 1);
-            let b_llval = bx.to_immediate_scalar(b_llval, b);
             OperandValue::Pair(a_llval, b_llval)
         } else {
             OperandValue::Immediate(llval)
@@ -435,15 +431,14 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
                 let Abi::ScalarPair(a_scalar, b_scalar) = dest.layout.abi else {
                     bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout);
                 };
-                let ty = bx.backend_type(dest.layout);
                 let b_offset = a_scalar.size(bx).align_to(b_scalar.align(bx).abi);
 
-                let llptr = bx.struct_gep(ty, dest.llval, 0);
                 let val = bx.from_immediate(a);
                 let align = dest.align;
-                bx.store_with_flags(val, llptr, align, flags);
+                bx.store_with_flags(val, dest.llval, align, flags);
 
-                let llptr = bx.struct_gep(ty, dest.llval, 1);
+                let llptr =
+                    bx.inbounds_gep(bx.type_i8(), dest.llval, &[bx.const_usize(b_offset.bytes())]);
                 let val = bx.from_immediate(b);
                 let align = dest.align.restrict_for_offset(b_offset);
                 bx.store_with_flags(val, llptr, align, flags);
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index c0bb3ac5661..73c08e2ca61 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -108,20 +108,17 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
                     // Also handles the first field of Scalar, ScalarPair, and Vector layouts.
                     self.llval
                 }
-                Abi::ScalarPair(a, b)
-                    if offset == a.size(bx.cx()).align_to(b.align(bx.cx()).abi) =>
-                {
-                    // Offset matches second field.
-                    let ty = bx.backend_type(self.layout);
-                    bx.struct_gep(ty, self.llval, 1)
+                Abi::ScalarPair(..) => {
+                    // FIXME(nikic): Generate this for all ABIs.
+                    bx.inbounds_gep(bx.type_i8(), self.llval, &[bx.const_usize(offset.bytes())])
                 }
-                Abi::Scalar(_) | Abi::ScalarPair(..) | Abi::Vector { .. } if field.is_zst() => {
+                Abi::Scalar(_) | Abi::Vector { .. } if field.is_zst() => {
                     // ZST fields (even some that require alignment) are not included in Scalar,
                     // ScalarPair, and Vector layouts, so manually offset the pointer.
                     bx.gep(bx.cx().type_i8(), self.llval, &[bx.const_usize(offset.bytes())])
                 }
-                Abi::Scalar(_) | Abi::ScalarPair(..) => {
-                    // All fields of Scalar and ScalarPair layouts must have been handled by this point.
+                Abi::Scalar(_) => {
+                    // All fields of Scalar layouts must have been handled by this point.
                     // Vector layouts have additional fields for each element of the vector, so don't panic in that case.
                     bug!(
                         "offset of non-ZST field `{:?}` does not match layout `{:#?}`",
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index 031fcc0adb1..0fef6bc110e 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -1,8 +1,8 @@
 use crate::errors;
 use rustc_ast::ast;
 use rustc_attr::InstructionSetAttr;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::fx::FxIndexSet;
+use rustc_data_structures::unord::UnordMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -18,7 +18,7 @@ use rustc_span::Span;
 pub fn from_target_feature(
     tcx: TyCtxt<'_>,
     attr: &ast::Attribute,
-    supported_target_features: &FxHashMap<String, Option<Symbol>>,
+    supported_target_features: &UnordMap<String, Option<Symbol>>,
     target_features: &mut Vec<Symbol>,
 ) {
     let Some(list) = attr.meta_item_list() else { return };
@@ -27,7 +27,7 @@ pub fn from_target_feature(
         let code = "enable = \"..\"";
         tcx.dcx()
             .struct_span_err(span, msg)
-            .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
+            .with_span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
             .emit();
     };
     let rust_features = tcx.features();
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 7fb5f10c6ca..6947ace17c5 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -391,10 +391,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                 if ecx.tcx.is_ctfe_mir_available(def) {
                     Ok(ecx.tcx.mir_for_ctfe(def))
                 } else if ecx.tcx.def_kind(def) == DefKind::AssocConst {
-                    let guar = ecx.tcx.dcx().span_delayed_bug(
-                        rustc_span::DUMMY_SP,
-                        "This is likely a const item that is missing from its impl",
-                    );
+                    let guar = ecx
+                        .tcx
+                        .dcx()
+                        .delayed_bug("This is likely a const item that is missing from its impl");
                     throw_inval!(AlreadyReported(guar.into()));
                 } else {
                     // `find_mir_or_eval_fn` checks that this is a const fn before even calling us,
@@ -630,7 +630,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                 // current number of evaluated terminators is a power of 2. The latter gives us a cheap
                 // way to implement exponential backoff.
                 let span = ecx.cur_span();
-                ecx.tcx.dcx().emit_warning(LongRunningWarn { span, item_span: ecx.tcx.span });
+                ecx.tcx.dcx().emit_warn(LongRunningWarn { span, item_span: ecx.tcx.span });
             }
         }
 
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 110ff87e27e..171cc89d6ad 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -518,7 +518,7 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             Ub(_) => {}
             Custom(custom) => {
                 (custom.add_args)(&mut |name, value| {
-                    builder.set_arg(name, value);
+                    builder.arg(name, value);
                 });
             }
             ValidationError(e) => e.add_args(dcx, builder),
@@ -536,65 +536,65 @@ impl<'a> ReportErrorExt for UndefinedBehaviorInfo<'a> {
             | UninhabitedEnumVariantWritten(_)
             | UninhabitedEnumVariantRead(_) => {}
             BoundsCheckFailed { len, index } => {
-                builder.set_arg("len", len);
-                builder.set_arg("index", index);
+                builder.arg("len", len);
+                builder.arg("index", index);
             }
             UnterminatedCString(ptr) | InvalidFunctionPointer(ptr) | InvalidVTablePointer(ptr) => {
-                builder.set_arg("pointer", ptr);
+                builder.arg("pointer", ptr);
             }
             PointerUseAfterFree(alloc_id, msg) => {
                 builder
-                    .set_arg("alloc_id", alloc_id)
-                    .set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
+                    .arg("alloc_id", alloc_id)
+                    .arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             PointerOutOfBounds { alloc_id, alloc_size, ptr_offset, ptr_size, msg } => {
                 builder
-                    .set_arg("alloc_id", alloc_id)
-                    .set_arg("alloc_size", alloc_size.bytes())
-                    .set_arg("ptr_offset", ptr_offset)
-                    .set_arg("ptr_size", ptr_size.bytes())
-                    .set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
+                    .arg("alloc_id", alloc_id)
+                    .arg("alloc_size", alloc_size.bytes())
+                    .arg("ptr_offset", ptr_offset)
+                    .arg("ptr_size", ptr_size.bytes())
+                    .arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             DanglingIntPointer(ptr, msg) => {
                 if ptr != 0 {
-                    builder.set_arg("pointer", format!("{ptr:#x}[noalloc]"));
+                    builder.arg("pointer", format!("{ptr:#x}[noalloc]"));
                 }
 
-                builder.set_arg("bad_pointer_message", bad_pointer_message(msg, dcx));
+                builder.arg("bad_pointer_message", bad_pointer_message(msg, dcx));
             }
             AlignmentCheckFailed(Misalignment { required, has }, msg) => {
-                builder.set_arg("required", required.bytes());
-                builder.set_arg("has", has.bytes());
-                builder.set_arg("msg", format!("{msg:?}"));
+                builder.arg("required", required.bytes());
+                builder.arg("has", has.bytes());
+                builder.arg("msg", format!("{msg:?}"));
             }
             WriteToReadOnly(alloc) | DerefFunctionPointer(alloc) | DerefVTablePointer(alloc) => {
-                builder.set_arg("allocation", alloc);
+                builder.arg("allocation", alloc);
             }
             InvalidBool(b) => {
-                builder.set_arg("value", format!("{b:02x}"));
+                builder.arg("value", format!("{b:02x}"));
             }
             InvalidChar(c) => {
-                builder.set_arg("value", format!("{c:08x}"));
+                builder.arg("value", format!("{c:08x}"));
             }
             InvalidTag(tag) => {
-                builder.set_arg("tag", format!("{tag:x}"));
+                builder.arg("tag", format!("{tag:x}"));
             }
             InvalidStr(err) => {
-                builder.set_arg("err", format!("{err}"));
+                builder.arg("err", format!("{err}"));
             }
             InvalidUninitBytes(Some((alloc, info))) => {
-                builder.set_arg("alloc", alloc);
-                builder.set_arg("access", info.access);
-                builder.set_arg("uninit", info.bad);
+                builder.arg("alloc", alloc);
+                builder.arg("access", info.access);
+                builder.arg("uninit", info.bad);
             }
             ScalarSizeMismatch(info) => {
-                builder.set_arg("target_size", info.target_size);
-                builder.set_arg("data_size", info.data_size);
+                builder.arg("target_size", info.target_size);
+                builder.arg("data_size", info.data_size);
             }
             AbiMismatchArgument { caller_ty, callee_ty }
             | AbiMismatchReturn { caller_ty, callee_ty } => {
-                builder.set_arg("caller_ty", caller_ty.to_string());
-                builder.set_arg("callee_ty", callee_ty.to_string());
+                builder.arg("caller_ty", caller_ty.to_string());
+                builder.arg("callee_ty", callee_ty.to_string());
             }
         }
     }
@@ -695,7 +695,7 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             )
         };
 
-        err.set_arg("front_matter", message);
+        err.arg("front_matter", message);
 
         fn add_range_arg<G: EmissionGuarantee>(
             r: WrappingRange,
@@ -725,12 +725,12 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
             ];
             let args = args.iter().map(|(a, b)| (a, b));
             let message = dcx.eagerly_translate_to_string(msg, args);
-            err.set_arg("in_range", message);
+            err.arg("in_range", message);
         }
 
         match self.kind {
             PtrToUninhabited { ty, .. } | UninhabitedVal { ty } => {
-                err.set_arg("ty", ty);
+                err.arg("ty", ty);
             }
             PointerAsInt { expected } | Uninit { expected } => {
                 let msg = match expected {
@@ -747,28 +747,28 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> {
                     ExpectedKind::Str => fluent::const_eval_validation_expected_str,
                 };
                 let msg = dcx.eagerly_translate_to_string(msg, [].into_iter());
-                err.set_arg("expected", msg);
+                err.arg("expected", msg);
             }
             InvalidEnumTag { value }
             | InvalidVTablePtr { value }
             | InvalidBool { value }
             | InvalidChar { value }
             | InvalidFnPtr { value } => {
-                err.set_arg("value", value);
+                err.arg("value", value);
             }
             NullablePtrOutOfRange { range, max_value } | PtrOutOfRange { range, max_value } => {
                 add_range_arg(range, max_value, dcx, err)
             }
             OutOfRange { range, max_value, value } => {
-                err.set_arg("value", value);
+                err.arg("value", value);
                 add_range_arg(range, max_value, dcx, err);
             }
             UnalignedPtr { required_bytes, found_bytes, .. } => {
-                err.set_arg("required_bytes", required_bytes);
-                err.set_arg("found_bytes", found_bytes);
+                err.arg("required_bytes", required_bytes);
+                err.arg("found_bytes", found_bytes);
             }
             DanglingPtrNoProvenance { pointer, .. } => {
-                err.set_arg("pointer", pointer);
+                err.arg("pointer", pointer);
             }
             NullPtr { .. }
             | PtrToStatic { .. }
@@ -814,10 +814,10 @@ impl ReportErrorExt for UnsupportedOpInfo {
             // print. So it's not worth the effort of having diagnostics that can print the `info`.
             UnsizedLocal | Unsupported(_) | ReadPointerAsInt(_) => {}
             OverwritePartialPointer(ptr) | ReadPartialPointer(ptr) => {
-                builder.set_arg("ptr", ptr);
+                builder.arg("ptr", ptr);
             }
             ThreadLocalStatic(did) | ReadExternStatic(did) => {
-                builder.set_arg("did", format!("{did:?}"));
+                builder.arg("did", format!("{did:?}"));
             }
         }
     }
@@ -844,7 +844,7 @@ impl<'tcx> ReportErrorExt for InterpError<'tcx> {
             InterpError::InvalidProgram(e) => e.add_args(dcx, builder),
             InterpError::ResourceExhaustion(e) => e.add_args(dcx, builder),
             InterpError::MachineStop(e) => e.add_args(&mut |name, value| {
-                builder.set_arg(name, value);
+                builder.arg(name, value);
             }),
         }
     }
@@ -880,15 +880,15 @@ impl<'tcx> ReportErrorExt for InvalidProgramInfo<'tcx> {
                 let diag: DiagnosticBuilder<'_, ()> =
                     e.into_diagnostic().into_diagnostic(dcx, dummy_level);
                 for (name, val) in diag.args() {
-                    builder.set_arg(name.clone(), val.clone());
+                    builder.arg(name.clone(), val.clone());
                 }
                 diag.cancel();
             }
             InvalidProgramInfo::FnAbiAdjustForForeignAbi(
                 AdjustForForeignAbiError::Unsupported { arch, abi },
             ) => {
-                builder.set_arg("arch", arch);
-                builder.set_arg("abi", abi.name());
+                builder.arg("arch", arch);
+                builder.arg("abi", abi.name());
             }
         }
     }
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 92955c4ed14..738c532964a 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -338,7 +338,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
             return;
         }
 
-        let mut err = op.build_error(self.ccx, span);
+        let err = op.build_error(self.ccx, span);
         assert!(err.is_error());
 
         match op.importance() {
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 7e1cbfe6667..1efa52df581 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/qualifs.rs
@@ -157,9 +157,7 @@ impl Qualif for NeedsNonConstDrop {
         // FIXME(effects): If `destruct` is not a `const_trait`,
         // or effects are disabled in this crate, then give up.
         let destruct_def_id = cx.tcx.require_lang_item(LangItem::Destruct, Some(cx.body.span));
-        if cx.tcx.generics_of(destruct_def_id).host_effect_index.is_none()
-            || !cx.tcx.features().effects
-        {
+        if !cx.tcx.has_host_param(destruct_def_id) || !cx.tcx.features().effects {
             return NeedsDrop::in_any_value_of_ty(cx, ty);
         }
 
diff --git a/compiler/rustc_const_eval/src/transform/mod.rs b/compiler/rustc_const_eval/src/transform/mod.rs
index a2928bdf51b..e3582c7d317 100644
--- a/compiler/rustc_const_eval/src/transform/mod.rs
+++ b/compiler/rustc_const_eval/src/transform/mod.rs
@@ -1,3 +1,2 @@
 pub mod check_consts;
-pub mod promote_consts;
 pub mod validate;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index b249ffb84b3..0b73691204d 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -74,7 +74,6 @@ impl<'tcx> MirPass<'tcx> for Validator {
             mir_phase,
             unwind_edge_count: 0,
             reachable_blocks: traversal::reachable_as_bitset(body),
-            place_cache: FxHashSet::default(),
             value_cache: FxHashSet::default(),
             can_unwind,
         };
@@ -106,7 +105,6 @@ struct CfgChecker<'a, 'tcx> {
     mir_phase: MirPhase,
     unwind_edge_count: usize,
     reachable_blocks: BitSet<BasicBlock>,
-    place_cache: FxHashSet<PlaceRef<'tcx>>,
     value_cache: FxHashSet<u128>,
     // If `false`, then the MIR must not contain `UnwindAction::Continue` or
     // `TerminatorKind::Resume`.
@@ -294,19 +292,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
 
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
         match &statement.kind {
-            StatementKind::Assign(box (dest, rvalue)) => {
-                // FIXME(JakobDegen): Check this for all rvalues, not just this one.
-                if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
-                    // The sides of an assignment must not alias. Currently this just checks whether
-                    // the places are identical.
-                    if dest == src {
-                        self.fail(
-                            location,
-                            "encountered `Assign` statement with overlapping memory",
-                        );
-                    }
-                }
-            }
             StatementKind::AscribeUserType(..) => {
                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
                     self.fail(
@@ -341,7 +326,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                     self.fail(location, format!("explicit `{kind:?}` is forbidden"));
                 }
             }
-            StatementKind::StorageLive(_)
+            StatementKind::Assign(..)
+            | StatementKind::StorageLive(_)
             | StatementKind::StorageDead(_)
             | StatementKind::Intrinsic(_)
             | StatementKind::Coverage(_)
@@ -404,10 +390,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                 }
 
                 // The call destination place and Operand::Move place used as an argument might be
-                // passed by a reference to the callee. Consequently they must be non-overlapping
-                // and cannot be packed. Currently this simply checks for duplicate places.
-                self.place_cache.clear();
-                self.place_cache.insert(destination.as_ref());
+                // passed by a reference to the callee. Consequently they cannot be packed.
                 if is_within_packed(self.tcx, &self.body.local_decls, *destination).is_some() {
                     // This is bad! The callee will expect the memory to be aligned.
                     self.fail(
@@ -418,10 +401,8 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                         ),
                     );
                 }
-                let mut has_duplicates = false;
                 for arg in args {
                     if let Operand::Move(place) = arg {
-                        has_duplicates |= !self.place_cache.insert(place.as_ref());
                         if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
                             // This is bad! The callee will expect the memory to be aligned.
                             self.fail(
@@ -434,16 +415,6 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                         }
                     }
                 }
-
-                if has_duplicates {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
             }
             TerminatorKind::Assert { target, unwind, .. } => {
                 self.check_edge(location, *target, EdgeKind::Normal);
@@ -1112,17 +1083,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         )
                     }
                 }
-                // FIXME(JakobDegen): Check this for all rvalues, not just this one.
-                if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
-                    // The sides of an assignment must not alias. Currently this just checks whether
-                    // the places are identical.
-                    if dest == src {
-                        self.fail(
-                            location,
-                            "encountered `Assign` statement with overlapping memory",
-                        );
-                    }
-                }
             }
             StatementKind::AscribeUserType(..) => {
                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
diff --git a/compiler/rustc_data_structures/src/fx.rs b/compiler/rustc_data_structures/src/fx.rs
index 9fce0e1e65c..80e72250470 100644
--- a/compiler/rustc_data_structures/src/fx.rs
+++ b/compiler/rustc_data_structures/src/fx.rs
@@ -7,6 +7,7 @@ pub type StdEntry<'a, K, V> = std::collections::hash_map::Entry<'a, K, V>;
 pub type FxIndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
 pub type FxIndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
 pub type IndexEntry<'a, K, V> = indexmap::map::Entry<'a, K, V>;
+pub type IndexOccupiedEntry<'a, K, V> = indexmap::map::OccupiedEntry<'a, K, V>;
 
 #[macro_export]
 macro_rules! define_id_collections {
diff --git a/compiler/rustc_data_structures/src/graph/dominators/mod.rs b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
index 4b819e1cbd6..a45f1dd72a1 100644
--- a/compiler/rustc_data_structures/src/graph/dominators/mod.rs
+++ b/compiler/rustc_data_structures/src/graph/dominators/mod.rs
@@ -394,6 +394,7 @@ impl<Node: Idx> Dominators<Node> {
     /// # Panics
     ///
     /// Panics if `b` is unreachable.
+    #[inline]
     pub fn dominates(&self, a: Node, b: Node) -> bool {
         match &self.kind {
             Kind::Path => a.index() <= b.index(),
diff --git a/compiler/rustc_data_structures/src/sorted_map.rs b/compiler/rustc_data_structures/src/sorted_map.rs
index ed2e558bffa..1436628139f 100644
--- a/compiler/rustc_data_structures/src/sorted_map.rs
+++ b/compiler/rustc_data_structures/src/sorted_map.rs
@@ -16,7 +16,7 @@ pub use index_map::SortedIndexMultiMap;
 /// stores data in a more compact way. It also supports accessing contiguous
 /// ranges of elements as a slice, and slices of already sorted elements can be
 /// inserted efficiently.
-#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable_Generic, Decodable_Generic)]
 pub struct SortedMap<K, V> {
     data: Vec<(K, V)>,
 }
diff --git a/compiler/rustc_data_structures/src/stable_hasher.rs b/compiler/rustc_data_structures/src/stable_hasher.rs
index 6d75b0fb8a0..afe26f80de8 100644
--- a/compiler/rustc_data_structures/src/stable_hasher.rs
+++ b/compiler/rustc_data_structures/src/stable_hasher.rs
@@ -245,6 +245,32 @@ unsafe impl<T: StableOrd> StableOrd for &T {
     const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
 }
 
+/// This is a companion trait to `StableOrd`. Some types like `Symbol` can be
+/// compared in a cross-session stable way, but their `Ord` implementation is
+/// not stable. In such cases, a `StableOrd` implementation can be provided
+/// to offer a lightweight way for stable sorting. (The more heavyweight option
+/// is to sort via `ToStableHashKey`, but then sorting needs to have access to
+/// a stable hashing context and `ToStableHashKey` can also be expensive as in
+/// the case of `Symbol` where it has to allocate a `String`.)
+///
+/// See the documentation of [StableOrd] for how stable sort order is defined.
+/// The same definition applies here. Be careful when implementing this trait.
+pub trait StableCompare {
+    const CAN_USE_UNSTABLE_SORT: bool;
+
+    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering;
+}
+
+/// `StableOrd` denotes that the type's `Ord` implementation is stable, so
+/// we can implement `StableCompare` by just delegating to `Ord`.
+impl<T: StableOrd> StableCompare for T {
+    const CAN_USE_UNSTABLE_SORT: bool = T::CAN_USE_UNSTABLE_SORT;
+
+    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.cmp(other)
+    }
+}
+
 /// Implement HashStable by just calling `Hash::hash()`. Also implement `StableOrd` for the type since
 /// that has the same requirements.
 ///
diff --git a/compiler/rustc_data_structures/src/svh.rs b/compiler/rustc_data_structures/src/svh.rs
index 71679086f16..1cfc9fecd47 100644
--- a/compiler/rustc_data_structures/src/svh.rs
+++ b/compiler/rustc_data_structures/src/svh.rs
@@ -10,7 +10,7 @@ use std::fmt;
 
 use crate::stable_hasher;
 
-#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable, Decodable, Hash)]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Encodable_Generic, Decodable_Generic, Hash)]
 pub struct Svh {
     hash: Fingerprint,
 }
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 43221d70e21..48edfba8da0 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -56,9 +56,6 @@ mod parallel;
 pub use parallel::scope;
 pub use parallel::{join, par_for_each_in, par_map, parallel_guard, try_par_for_each_in};
 
-pub use std::sync::atomic::Ordering;
-pub use std::sync::atomic::Ordering::SeqCst;
-
 pub use vec::{AppendOnlyIndexVec, AppendOnlyVec};
 
 mod vec;
@@ -67,8 +64,7 @@ mod freeze;
 pub use freeze::{FreezeLock, FreezeReadGuard, FreezeWriteGuard};
 
 mod mode {
-    use super::Ordering;
-    use std::sync::atomic::AtomicU8;
+    use std::sync::atomic::{AtomicU8, Ordering};
 
     const UNINITIALIZED: u8 = 0;
     const DYN_NOT_THREAD_SAFE: u8 = 1;
@@ -113,6 +109,7 @@ cfg_match! {
     cfg(not(parallel_compiler)) => {
         use std::ops::Add;
         use std::cell::Cell;
+        use std::sync::atomic::Ordering;
 
         pub unsafe auto trait Send {}
         pub unsafe auto trait Sync {}
diff --git a/compiler/rustc_data_structures/src/unord.rs b/compiler/rustc_data_structures/src/unord.rs
index 47c56eba7ad..907c866b3ed 100644
--- a/compiler/rustc_data_structures/src/unord.rs
+++ b/compiler/rustc_data_structures/src/unord.rs
@@ -3,9 +3,8 @@
 //! as required by the query system.
 
 use rustc_hash::{FxHashMap, FxHashSet};
-use smallvec::SmallVec;
 use std::{
-    borrow::Borrow,
+    borrow::{Borrow, BorrowMut},
     collections::hash_map::Entry,
     hash::Hash,
     iter::{Product, Sum},
@@ -14,7 +13,7 @@ use std::{
 
 use crate::{
     fingerprint::Fingerprint,
-    stable_hasher::{HashStable, StableHasher, StableOrd, ToStableHashKey},
+    stable_hasher::{HashStable, StableCompare, StableHasher, ToStableHashKey},
 };
 
 /// `UnordItems` is the order-less version of `Iterator`. It only contains methods
@@ -134,36 +133,78 @@ impl<'a, T: Copy + 'a, I: Iterator<Item = &'a T>> UnordItems<&'a T, I> {
     }
 }
 
-impl<T: Ord, I: Iterator<Item = T>> UnordItems<T, I> {
+impl<T, I: Iterator<Item = T>> UnordItems<T, I> {
+    #[inline]
     pub fn into_sorted<HCX>(self, hcx: &HCX) -> Vec<T>
     where
         T: ToStableHashKey<HCX>,
     {
-        let mut items: Vec<T> = self.0.collect();
-        items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
-        items
+        self.collect_sorted(hcx, true)
     }
 
     #[inline]
     pub fn into_sorted_stable_ord(self) -> Vec<T>
     where
-        T: Ord + StableOrd,
+        T: StableCompare,
+    {
+        self.collect_stable_ord_by_key(|x| x)
+    }
+
+    #[inline]
+    pub fn into_sorted_stable_ord_by_key<K, C>(self, project_to_key: C) -> Vec<T>
+    where
+        K: StableCompare,
+        C: for<'a> Fn(&'a T) -> &'a K,
     {
-        let mut items: Vec<T> = self.0.collect();
-        if !T::CAN_USE_UNSTABLE_SORT {
-            items.sort();
-        } else {
-            items.sort_unstable()
+        self.collect_stable_ord_by_key(project_to_key)
+    }
+
+    #[inline]
+    pub fn collect_sorted<HCX, C>(self, hcx: &HCX, cache_sort_key: bool) -> C
+    where
+        T: ToStableHashKey<HCX>,
+        C: FromIterator<T> + BorrowMut<[T]>,
+    {
+        let mut items: C = self.0.collect();
+
+        let slice = items.borrow_mut();
+        if slice.len() > 1 {
+            if cache_sort_key {
+                slice.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
+            } else {
+                slice.sort_by_key(|x| x.to_stable_hash_key(hcx));
+            }
         }
+
         items
     }
 
-    pub fn into_sorted_small_vec<HCX, const LEN: usize>(self, hcx: &HCX) -> SmallVec<[T; LEN]>
+    #[inline]
+    pub fn collect_stable_ord_by_key<K, C, P>(self, project_to_key: P) -> C
     where
-        T: ToStableHashKey<HCX>,
+        K: StableCompare,
+        P: for<'a> Fn(&'a T) -> &'a K,
+        C: FromIterator<T> + BorrowMut<[T]>,
     {
-        let mut items: SmallVec<[T; LEN]> = self.0.collect();
-        items.sort_by_cached_key(|x| x.to_stable_hash_key(hcx));
+        let mut items: C = self.0.collect();
+
+        let slice = items.borrow_mut();
+        if slice.len() > 1 {
+            if !K::CAN_USE_UNSTABLE_SORT {
+                slice.sort_by(|a, b| {
+                    let a_key = project_to_key(a);
+                    let b_key = project_to_key(b);
+                    a_key.stable_cmp(b_key)
+                });
+            } else {
+                slice.sort_unstable_by(|a, b| {
+                    let a_key = project_to_key(a);
+                    let b_key = project_to_key(b);
+                    a_key.stable_cmp(b_key)
+                });
+            }
+        }
+
         items
     }
 }
@@ -185,7 +226,7 @@ trait UnordCollection {}
 ///
 /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533)
 /// for more information.
-#[derive(Debug, Eq, PartialEq, Clone, Encodable, Decodable)]
+#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)]
 pub struct UnordSet<V: Eq + Hash> {
     inner: FxHashSet<V>,
 }
@@ -268,16 +309,30 @@ impl<V: Eq + Hash> UnordSet<V> {
     }
 
     /// Returns the items of this set in stable sort order (as defined by
-    /// `StableOrd`). This method is much more efficient than
+    /// `StableCompare`). This method is much more efficient than
     /// `into_sorted` because it does not need to transform keys to their
     /// `ToStableHashKey` equivalent.
     #[inline]
-    pub fn to_sorted_stable_ord(&self) -> Vec<V>
+    pub fn to_sorted_stable_ord(&self) -> Vec<&V>
     where
-        V: Ord + StableOrd + Clone,
+        V: StableCompare,
     {
-        let mut items: Vec<V> = self.inner.iter().cloned().collect();
-        items.sort_unstable();
+        let mut items: Vec<&V> = self.inner.iter().collect();
+        items.sort_unstable_by(|a, b| a.stable_cmp(*b));
+        items
+    }
+
+    /// Returns the items of this set in stable sort order (as defined by
+    /// `StableCompare`). This method is much more efficient than
+    /// `into_sorted` because it does not need to transform keys to their
+    /// `ToStableHashKey` equivalent.
+    #[inline]
+    pub fn into_sorted_stable_ord(self) -> Vec<V>
+    where
+        V: StableCompare,
+    {
+        let mut items: Vec<V> = self.inner.into_iter().collect();
+        items.sort_unstable_by(V::stable_cmp);
         items
     }
 
@@ -362,7 +417,7 @@ impl<HCX, V: Hash + Eq + HashStable<HCX>> HashStable<HCX> for UnordSet<V> {
 ///
 /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533)
 /// for more information.
-#[derive(Debug, Eq, PartialEq, Clone, Encodable, Decodable)]
+#[derive(Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)]
 pub struct UnordMap<K: Eq + Hash, V> {
     inner: FxHashMap<K, V>,
 }
@@ -483,16 +538,16 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
         to_sorted_vec(hcx, self.inner.iter(), cache_sort_key, |&(k, _)| k)
     }
 
-    /// Returns the entries of this map in stable sort order (as defined by `StableOrd`).
+    /// Returns the entries of this map in stable sort order (as defined by `StableCompare`).
     /// This method can be much more efficient than `into_sorted` because it does not need
     /// to transform keys to their `ToStableHashKey` equivalent.
     #[inline]
-    pub fn to_sorted_stable_ord(&self) -> Vec<(K, &V)>
+    pub fn to_sorted_stable_ord(&self) -> Vec<(&K, &V)>
     where
-        K: Ord + StableOrd + Copy,
+        K: StableCompare,
     {
-        let mut items: Vec<(K, &V)> = self.inner.iter().map(|(&k, v)| (k, v)).collect();
-        items.sort_unstable_by_key(|&(k, _)| k);
+        let mut items: Vec<_> = self.inner.iter().collect();
+        items.sort_unstable_by(|(a, _), (b, _)| a.stable_cmp(*b));
         items
     }
 
@@ -510,6 +565,19 @@ impl<K: Eq + Hash, V> UnordMap<K, V> {
         to_sorted_vec(hcx, self.inner.into_iter(), cache_sort_key, |(k, _)| k)
     }
 
+    /// Returns the entries of this map in stable sort order (as defined by `StableCompare`).
+    /// This method can be much more efficient than `into_sorted` because it does not need
+    /// to transform keys to their `ToStableHashKey` equivalent.
+    #[inline]
+    pub fn into_sorted_stable_ord(self) -> Vec<(K, V)>
+    where
+        K: StableCompare,
+    {
+        let mut items: Vec<(K, V)> = self.inner.into_iter().collect();
+        items.sort_unstable_by(|a, b| a.0.stable_cmp(&b.0));
+        items
+    }
+
     /// Returns the values of this map in stable sort order (as defined by K's
     /// `ToStableHashKey` implementation).
     ///
@@ -558,7 +626,7 @@ impl<HCX, K: Hash + Eq + HashStable<HCX>, V: HashStable<HCX>> HashStable<HCX> fo
 ///
 /// See [MCP 533](https://github.com/rust-lang/compiler-team/issues/533)
 /// for more information.
-#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable, Decodable)]
+#[derive(Default, Debug, Eq, PartialEq, Clone, Encodable_Generic, Decodable_Generic)]
 pub struct UnordBag<V> {
     inner: Vec<V>,
 }
diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml
index 49042984553..97a7dfef3b3 100644
--- a/compiler/rustc_driver_impl/Cargo.toml
+++ b/compiler/rustc_driver_impl/Cargo.toml
@@ -50,6 +50,7 @@ rustc_target = { path = "../rustc_target" }
 rustc_trait_selection = { path = "../rustc_trait_selection" }
 rustc_ty_utils = { path = "../rustc_ty_utils" }
 serde_json = "1.0.59"
+shlex = "1.0"
 time = { version = "0.3", default-features = false, features = ["alloc", "formatting"] }
 tracing = { version = "0.1.35" }
 # tidy-alphabetical-end
diff --git a/compiler/rustc_driver_impl/src/args.rs b/compiler/rustc_driver_impl/src/args.rs
index b7407f5a508..5dfd37a6da4 100644
--- a/compiler/rustc_driver_impl/src/args.rs
+++ b/compiler/rustc_driver_impl/src/args.rs
@@ -5,18 +5,92 @@ use std::io;
 
 use rustc_session::EarlyDiagCtxt;
 
-fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
-    if let Some(path) = arg.strip_prefix('@') {
-        let file = match fs::read_to_string(path) {
-            Ok(file) => file,
-            Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
-                return Err(Error::Utf8Error(Some(path.to_string())));
+/// Expands argfiles in command line arguments.
+#[derive(Default)]
+struct Expander {
+    shell_argfiles: bool,
+    next_is_unstable_option: bool,
+    expanded: Vec<String>,
+}
+
+impl Expander {
+    /// Handles the next argument. If the argument is an argfile, it is expanded
+    /// inline.
+    fn arg(&mut self, arg: &str) -> Result<(), Error> {
+        if let Some(argfile) = arg.strip_prefix('@') {
+            match argfile.split_once(':') {
+                Some(("shell", path)) if self.shell_argfiles => {
+                    shlex::split(&Self::read_file(path)?)
+                        .ok_or_else(|| Error::ShellParseError(path.to_string()))?
+                        .into_iter()
+                        .for_each(|arg| self.push(arg));
+                }
+                _ => {
+                    let contents = Self::read_file(argfile)?;
+                    contents.lines().for_each(|arg| self.push(arg.to_string()));
+                }
+            }
+        } else {
+            self.push(arg.to_string());
+        }
+
+        Ok(())
+    }
+
+    /// Adds a command line argument verbatim with no argfile expansion.
+    fn push(&mut self, arg: String) {
+        // Unfortunately, we have to do some eager argparsing to handle unstable
+        // options which change the behavior of argfile arguments.
+        //
+        // Normally, all of the argfile arguments (e.g. `@args.txt`) are
+        // expanded into our arguments list *and then* the whole list of
+        // arguments are passed on to be parsed. However, argfile parsing
+        // options like `-Zshell_argfiles` need to change the behavior of that
+        // argument expansion. So we have to do a little parsing on our own here
+        // instead of leaning on the existing logic.
+        //
+        // All we care about are unstable options, so we parse those out and
+        // look for any that affect how we expand argfiles. This argument
+        // inspection is very conservative; we only change behavior when we see
+        // exactly the options we're looking for and everything gets passed
+        // through.
+
+        if self.next_is_unstable_option {
+            self.inspect_unstable_option(&arg);
+            self.next_is_unstable_option = false;
+        } else if let Some(unstable_option) = arg.strip_prefix("-Z") {
+            if unstable_option.is_empty() {
+                self.next_is_unstable_option = true;
+            } else {
+                self.inspect_unstable_option(unstable_option);
+            }
+        }
+
+        self.expanded.push(arg);
+    }
+
+    /// Consumes the `Expander`, returning the expanded arguments.
+    fn finish(self) -> Vec<String> {
+        self.expanded
+    }
+
+    /// Parses any relevant unstable flags specified on the command line.
+    fn inspect_unstable_option(&mut self, option: &str) {
+        match option {
+            "shell-argfiles" => self.shell_argfiles = true,
+            _ => (),
+        }
+    }
+
+    /// Reads the contents of a file as UTF-8.
+    fn read_file(path: &str) -> Result<String, Error> {
+        fs::read_to_string(path).map_err(|e| {
+            if e.kind() == io::ErrorKind::InvalidData {
+                Error::Utf8Error(Some(path.to_string()))
+            } else {
+                Error::IOError(path.to_string(), e)
             }
-            Err(err) => return Err(Error::IOError(path.to_string(), err)),
-        };
-        Ok(file.lines().map(ToString::to_string).collect())
-    } else {
-        Ok(vec![arg])
+        })
     }
 }
 
@@ -24,20 +98,20 @@ fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
 /// If this function is intended to be used with command line arguments,
 /// `argv[0]` must be removed prior to calling it manually.
 pub fn arg_expand_all(early_dcx: &EarlyDiagCtxt, at_args: &[String]) -> Vec<String> {
-    let mut args = Vec::new();
+    let mut expander = Expander::default();
     for arg in at_args {
-        match arg_expand(arg.clone()) {
-            Ok(arg) => args.extend(arg),
-            Err(err) => early_dcx.early_fatal(format!("Failed to load argument file: {err}")),
+        if let Err(err) = expander.arg(arg) {
+            early_dcx.early_fatal(format!("Failed to load argument file: {err}"));
         }
     }
-    args
+    expander.finish()
 }
 
 #[derive(Debug)]
 pub enum Error {
     Utf8Error(Option<String>),
     IOError(String, io::Error),
+    ShellParseError(String),
 }
 
 impl fmt::Display for Error {
@@ -46,6 +120,7 @@ impl fmt::Display for Error {
             Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
             Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
             Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
+            Error::ShellParseError(path) => write!(fmt, "Invalid shell-style arguments in {path}"),
         }
     }
 }
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index ca6b0afc76a..2e4baf26176 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -12,6 +12,7 @@
 #![feature(lazy_cell)]
 #![feature(let_chains)]
 #![feature(panic_update_hook)]
+#![feature(result_flattening)]
 #![recursion_limit = "256"]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
@@ -24,7 +25,6 @@ use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{
     get_resident_set_size, print_time_passes_entry, TimePassesFormat,
 };
-use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
 use rustc_errors::{markdown, ColorConfig};
 use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult};
@@ -475,7 +475,7 @@ fn run_compiler(
             eprintln!(
                 "Fuel used by {}: {}",
                 sess.opts.unstable_opts.print_fuel.as_ref().unwrap(),
-                sess.print_fuel.load(SeqCst)
+                sess.print_fuel.load(Ordering::SeqCst)
             );
         }
 
@@ -715,7 +715,7 @@ fn print_crate_info(
         let result = parse_crate_attrs(sess);
         match result {
             Ok(attrs) => Some(attrs),
-            Err(mut parse_error) => {
+            Err(parse_error) => {
                 parse_error.emit();
                 return Compilation::Stop;
             }
@@ -1249,8 +1249,7 @@ pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuarantee
 /// Variant of `catch_fatal_errors` for the `interface::Result` return type
 /// that also computes the exit code.
 pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
-    let result = catch_fatal_errors(f).and_then(|result| result);
-    match result {
+    match catch_fatal_errors(f).flatten() {
         Ok(()) => EXIT_SUCCESS,
         Err(_) => EXIT_FAILURE,
     }
@@ -1393,7 +1392,7 @@ fn report_ice(
 ) {
     let fallback_bundle =
         rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
-    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
+    let emitter = Box::new(rustc_errors::emitter::HumanEmitter::stderr(
         rustc_errors::ColorConfig::Auto,
         fallback_bundle,
     ));
@@ -1430,7 +1429,7 @@ fn report_ice(
             }
             Err(err) => {
                 // The path ICE couldn't be written to disk, provide feedback to the user as to why.
-                dcx.emit_warning(session_diagnostics::IcePathError {
+                dcx.emit_warn(session_diagnostics::IcePathError {
                     path: path.clone(),
                     error: err.to_string(),
                     env_var: std::env::var_os("RUSTC_ICE")
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index 1028d43f9c5..9cd9ed54d41 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -515,6 +515,8 @@ E0792: include_str!("./error_codes/E0792.md"),
 E0793: include_str!("./error_codes/E0793.md"),
 E0794: include_str!("./error_codes/E0794.md"),
 E0795: include_str!("./error_codes/E0795.md"),
+E0796: include_str!("./error_codes/E0796.md"),
+E0797: include_str!("./error_codes/E0797.md"),
 }
 
 // Undocumented removed error codes. Note that many removed error codes are kept in the list above
diff --git a/compiler/rustc_error_codes/src/error_codes/E0733.md b/compiler/rustc_error_codes/src/error_codes/E0733.md
index 051b75148e5..42c01975dd8 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0733.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0733.md
@@ -10,35 +10,31 @@ async fn foo(n: usize) {
 }
 ```
 
-To perform async recursion, the `async fn` needs to be desugared such that the
-`Future` is explicit in the return type:
+The recursive invocation can be boxed:
 
-```edition2018,compile_fail,E0720
-use std::future::Future;
-fn foo_desugared(n: usize) -> impl Future<Output = ()> {
-    async move {
-        if n > 0 {
-            foo_desugared(n - 1).await;
-        }
+```edition2018
+async fn foo(n: usize) {
+    if n > 0 {
+        Box::pin(foo(n - 1)).await;
     }
 }
 ```
 
-Finally, the future is wrapped in a pinned box:
+The `Box<...>` ensures that the result is of known size, and the pin is
+required to keep it in the same place in memory.
+
+Alternatively, the body can be boxed:
 
 ```edition2018
 use std::future::Future;
 use std::pin::Pin;
-fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
+fn foo(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
     Box::pin(async move {
         if n > 0 {
-            foo_recursive(n - 1).await;
+            foo(n - 1).await;
         }
     })
 }
 ```
 
-The `Box<...>` ensures that the result is of known size, and the pin is
-required to keep it in the same place in memory.
-
 [`async`]: https://doc.rust-lang.org/std/keyword.async.html
diff --git a/compiler/rustc_error_codes/src/error_codes/E0796.md b/compiler/rustc_error_codes/src/error_codes/E0796.md
new file mode 100644
index 00000000000..cea18f8db85
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0796.md
@@ -0,0 +1,22 @@
+Reference of mutable static.
+
+Erroneous code example:
+
+```compile_fail,edition2024,E0796
+static mut X: i32 = 23;
+static mut Y: i32 = 24;
+
+unsafe {
+  let y = &X;
+  let ref x = X;
+  let (x, y) = (&X, &Y);
+  foo(&X);
+}
+
+fn foo<'a>(_x: &'a i32) {}
+```
+
+Mutable statics can be written to by multiple threads: aliasing violations or
+data races will cause undefined behavior.
+
+Reference of mutable static is a hard error from 2024 edition.
diff --git a/compiler/rustc_error_codes/src/error_codes/E0797.md b/compiler/rustc_error_codes/src/error_codes/E0797.md
new file mode 100644
index 00000000000..8a912307264
--- /dev/null
+++ b/compiler/rustc_error_codes/src/error_codes/E0797.md
@@ -0,0 +1,26 @@
+Struct update syntax was used without a base expression.
+
+Erroneous code example:
+
+```compile_fail,E0797
+struct Foo {
+    fizz: u8,
+    buzz: u8
+}
+
+let f1 = Foo { fizz: 10, buzz: 1};
+let f2 = Foo { fizz: 10, .. }; // error
+```
+
+Using struct update syntax requires a 'base expression'.
+This will be used to fill remaining fields.
+
+```
+struct Foo {
+    fizz: u8,
+    buzz: u8
+}
+
+let f1 = Foo { fizz: 10, buzz: 1};
+let f2 = Foo { fizz: 10, ..f1 };
+```
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 48e48f59a99..97f2efa7874 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -20,7 +20,7 @@ use rustc_span::source_map::SourceMap;
 use rustc_span::SourceFile;
 
 /// Generates diagnostics using annotate-snippet
-pub struct AnnotateSnippetEmitterWriter {
+pub struct AnnotateSnippetEmitter {
     source_map: Option<Lrc<SourceMap>>,
     fluent_bundle: Option<Lrc<FluentBundle>>,
     fallback_bundle: LazyFallbackBundle,
@@ -33,7 +33,7 @@ pub struct AnnotateSnippetEmitterWriter {
     macro_backtrace: bool,
 }
 
-impl Translate for AnnotateSnippetEmitterWriter {
+impl Translate for AnnotateSnippetEmitter {
     fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
         self.fluent_bundle.as_ref()
     }
@@ -43,7 +43,7 @@ impl Translate for AnnotateSnippetEmitterWriter {
     }
 }
 
-impl Emitter for AnnotateSnippetEmitterWriter {
+impl Emitter for AnnotateSnippetEmitter {
     /// The entry point for the diagnostics generation
     fn emit_diagnostic(&mut self, diag: &Diagnostic) {
         let fluent_args = to_fluent_args(diag.args());
@@ -86,10 +86,8 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
 /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
 fn annotation_type_for_level(level: Level) -> AnnotationType {
     match level {
-        Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
-            AnnotationType::Error
-        }
-        Level::Warning(_) => AnnotationType::Warning,
+        Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error => AnnotationType::Error,
+        Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
         Level::Note | Level::OnceNote => AnnotationType::Note,
         Level::Help | Level::OnceHelp => AnnotationType::Help,
         // FIXME(#59346): Not sure how to map this level
@@ -99,7 +97,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType {
     }
 }
 
-impl AnnotateSnippetEmitterWriter {
+impl AnnotateSnippetEmitter {
     pub fn new(
         source_map: Option<Lrc<SourceMap>>,
         fluent_bundle: Option<Lrc<FluentBundle>>,
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 49431fb7b3f..d8d6922a1bc 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -152,7 +152,6 @@ pub enum DiagnosticId {
         name: String,
         /// Indicates whether this lint should show up in cargo's future breakage report.
         has_future_breakage: bool,
-        is_force_warn: bool,
     },
 }
 
@@ -212,6 +211,9 @@ impl StringPart {
     }
 }
 
+// Note: most of these methods are setters that return `&mut Self`. The small
+// number of simple getter functions all have `get_` prefixes to distinguish
+// them from the setters.
 impl Diagnostic {
     #[track_caller]
     pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
@@ -241,13 +243,12 @@ impl Diagnostic {
 
     pub fn is_error(&self) -> bool {
         match self.level {
-            Level::Bug
-            | Level::DelayedBug
-            | Level::Fatal
-            | Level::Error { .. }
-            | Level::FailureNote => true,
+            Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error | Level::FailureNote => {
+                true
+            }
 
-            Level::Warning(_)
+            Level::ForceWarning(_)
+            | Level::Warning
             | Level::Note
             | Level::OnceNote
             | Level::Help
@@ -261,7 +262,7 @@ impl Diagnostic {
         &mut self,
         unstable_to_stable: &FxIndexMap<LintExpectationId, LintExpectationId>,
     ) {
-        if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
+        if let Level::Expect(expectation_id) | Level::ForceWarning(Some(expectation_id)) =
             &mut self.level
         {
             if expectation_id.is_stable() {
@@ -291,8 +292,11 @@ impl Diagnostic {
     }
 
     pub(crate) fn is_force_warn(&self) -> bool {
-        match self.code {
-            Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
+        match self.level {
+            Level::ForceWarning(_) => {
+                assert!(self.is_lint);
+                true
+            }
             _ => false,
         }
     }
@@ -308,25 +312,27 @@ impl Diagnostic {
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
     #[track_caller]
-    pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
+    pub fn downgrade_to_delayed_bug(&mut self) {
         assert!(
             self.is_error(),
             "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
             self.level
         );
         self.level = Level::DelayedBug;
-
-        self
     }
 
-    /// Adds a span/label to be included in the resulting snippet.
+    /// Appends a labeled span to the diagnostic.
     ///
-    /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
-    /// was first built. That means it will be shown together with the original
-    /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
+    /// Labels are used to convey additional context for the diagnostic's primary span. They will
+    /// be shown together with the original diagnostic's span, *not* with spans added by
+    /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because
+    /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed
+    /// either.
     ///
-    /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
-    /// the `Span` supplied when creating the diagnostic is primary.
+    /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when
+    /// the diagnostic was constructed. However, the label span is *not* considered a
+    /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
+    /// primary.
     #[rustc_lint_diagnostics]
     pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label));
@@ -344,7 +350,7 @@ impl Diagnostic {
 
     pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
         let before = self.span.clone();
-        self.set_span(after);
+        self.span(after);
         for span_label in before.span_labels() {
             if let Some(label) = span_label.label {
                 if span_label.is_primary && keep_label {
@@ -469,7 +475,7 @@ impl Diagnostic {
     /// Add a warning attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.sub(Level::Warning(None), msg, MultiSpan::new());
+        self.sub(Level::Warning, msg, MultiSpan::new());
         self
     }
 
@@ -481,7 +487,7 @@ impl Diagnostic {
         sp: S,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
-        self.sub(Level::Warning(None), msg, sp.into());
+        self.sub(Level::Warning, msg, sp.into());
         self
     }
 
@@ -876,7 +882,7 @@ impl Diagnostic {
         self
     }
 
-    pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
+    pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
         self.span = sp.into();
         if let Some(span) = self.span.primary_span() {
             self.sort_span = span;
@@ -884,7 +890,7 @@ impl Diagnostic {
         self
     }
 
-    pub fn set_is_lint(&mut self) -> &mut Self {
+    pub fn is_lint(&mut self) -> &mut Self {
         self.is_lint = true;
         self
     }
@@ -903,7 +909,7 @@ impl Diagnostic {
         self.code.clone()
     }
 
-    pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
+    pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
         self.messages[0] = (msg.into(), Style::NoStyle);
         self
     }
@@ -915,7 +921,7 @@ impl Diagnostic {
         self.args.iter()
     }
 
-    pub fn set_arg(
+    pub fn arg(
         &mut self,
         name: impl Into<Cow<'static, str>>,
         arg: impl IntoDiagnosticArg,
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index ae54d343dad..a02909f29c4 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -30,57 +30,44 @@ where
     G: EmissionGuarantee,
 {
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
-        let mut diag = self.node.into_diagnostic(dcx, level);
-        diag.set_span(self.span);
-        diag
+        self.node.into_diagnostic(dcx, level).with_span(self.span)
     }
 }
 
 /// Used for emitting structured error messages and other diagnostic information.
+/// Each constructed `DiagnosticBuilder` must be consumed by a function such as
+/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a
+/// `DiagnosticBuilder` is dropped without being consumed by one of these
+/// functions.
 ///
 /// If there is some state in a downstream crate you would like to
 /// access in the methods of `DiagnosticBuilder` here, consider
 /// extending `DiagCtxtFlags`.
 #[must_use]
-#[derive(Clone)]
 pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
-    state: DiagnosticBuilderState<'a>,
+    pub dcx: &'a DiagCtxt,
 
-    /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
-    /// return value, especially within the frequently-used `PResult` type.
-    /// In theory, return value optimization (RVO) should avoid unnecessary
-    /// copying. In practice, it does not (at the time of writing).
-    diagnostic: Box<Diagnostic>,
+    /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is
+    /// consumed via `emit`, `cancel`, etc. At that point it is consumed and
+    /// replaced with `None`. Then `drop` checks that it is `None`; if not, it
+    /// panics because a diagnostic was built but not used.
+    ///
+    /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is
+    /// often used as a return value, especially within the frequently-used
+    /// `PResult` type. In theory, return value optimization (RVO) should avoid
+    /// unnecessary copying. In practice, it does not (at the time of writing).
+    diag: Option<Box<Diagnostic>>,
 
     _marker: PhantomData<G>,
 }
 
-#[derive(Clone)]
-enum DiagnosticBuilderState<'a> {
-    /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
-    ///
-    /// The `Diagnostic` will be emitted through this `DiagCtxt`.
-    Emittable(&'a DiagCtxt),
+// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted
+// twice, which would be bad.
+impl<G> !Clone for DiagnosticBuilder<'_, G> {}
 
-    /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
-    ///
-    /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be
-    /// assumed that `.emit()` was previously called, to end up in this state.
-    ///
-    /// While this is also used by `.cancel()`, this state is only observed by
-    /// the `Drop` `impl` of `DiagnosticBuilder`, because `.cancel()` takes
-    /// `self` by-value specifically to prevent any attempts to `.emit()`.
-    ///
-    // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
-    // despite that being potentially lossy, if important information is added
-    // *after* the original `.emit()` call.
-    AlreadyEmittedOrDuringCancellation,
-}
-
-// `DiagnosticBuilderState` should be pointer-sized.
 rustc_data_structures::static_assert_size!(
-    DiagnosticBuilderState<'_>,
-    std::mem::size_of::<&DiagCtxt>()
+    DiagnosticBuilder<'_, ()>,
+    2 * std::mem::size_of::<usize>()
 );
 
 /// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
@@ -94,68 +81,50 @@ pub trait EmissionGuarantee: Sized {
     /// `impl` of `EmissionGuarantee`, to make it impossible to create a value
     /// of `Self::EmitResult` without actually performing the emission.
     #[track_caller]
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
 }
 
 impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
+    /// Takes the diagnostic. For use by methods that consume the
+    /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the
+    /// only code that will be run on `self`.
+    fn take_diag(&mut self) -> Diagnostic {
+        Box::into_inner(self.diag.take().unwrap())
+    }
+
     /// Most `emit_producing_guarantee` functions use this as a starting point.
-    fn emit_producing_nothing(&mut self) {
-        match self.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-                dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
-            }
-            // `.emit()` was previously called, disallowed from repeating it.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
-        }
+    fn emit_producing_nothing(mut self) {
+        let diag = self.take_diag();
+        self.dcx.emit_diagnostic(diag);
+    }
+
+    /// `ErrorGuaranteed::emit_producing_guarantee` uses this.
+    // FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
+    fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
+        let diag = self.take_diag();
+
+        // Only allow a guarantee if the `level` wasn't switched to a
+        // non-error. The field isn't `pub`, but the whole `Diagnostic` can be
+        // overwritten with a new one, thanks to `DerefMut`.
+        assert!(
+            diag.is_error(),
+            "emitted non-error ({:?}) diagnostic from `DiagnosticBuilder<ErrorGuaranteed>`",
+            diag.level,
+        );
+
+        let guar = self.dcx.emit_diagnostic(diag);
+        guar.unwrap()
     }
 }
 
-// FIXME(eddyb) make `ErrorGuaranteed` impossible to create outside `.emit()`.
 impl EmissionGuarantee for ErrorGuaranteed {
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        // Contrast this with `emit_producing_nothing`.
-        match db.state {
-            // First `.emit()` call, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                db.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
-                let guar = dcx.emit_diagnostic_without_consuming(&mut db.diagnostic);
-
-                // Only allow a guarantee if the `level` wasn't switched to a
-                // non-error - the field isn't `pub`, but the whole `Diagnostic`
-                // can be overwritten with a new one, thanks to `DerefMut`.
-                assert!(
-                    db.diagnostic.is_error(),
-                    "emitted non-error ({:?}) diagnostic \
-                     from `DiagnosticBuilder<ErrorGuaranteed>`",
-                    db.diagnostic.level,
-                );
-                guar.unwrap()
-            }
-            // `.emit()` was previously called, disallowed from repeating it,
-            // but can take advantage of the previous `.emit()`'s guarantee
-            // still being applicable (i.e. as a form of idempotency).
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
-                // Only allow a guarantee if the `level` wasn't switched to a
-                // non-error - the field isn't `pub`, but the whole `Diagnostic`
-                // can be overwritten with a new one, thanks to `DerefMut`.
-                assert!(
-                    db.diagnostic.is_error(),
-                    "`DiagnosticBuilder<ErrorGuaranteed>`'s diagnostic \
-                     became non-error ({:?}), after original `.emit()`",
-                    db.diagnostic.level,
-                );
-                #[allow(deprecated)]
-                ErrorGuaranteed::unchecked_claim_error_was_emitted()
-            }
-        }
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_error_guaranteed()
     }
 }
 
-// FIXME(eddyb) should there be a `Option<ErrorGuaranteed>` impl as well?
 impl EmissionGuarantee for () {
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
         db.emit_producing_nothing();
     }
 }
@@ -168,7 +137,7 @@ pub struct BugAbort;
 impl EmissionGuarantee for BugAbort {
     type EmitResult = !;
 
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
         db.emit_producing_nothing();
         panic::panic_any(ExplicitBug);
     }
@@ -182,37 +151,48 @@ pub struct FatalAbort;
 impl EmissionGuarantee for FatalAbort {
     type EmitResult = !;
 
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
         db.emit_producing_nothing();
         crate::FatalError.raise()
     }
 }
 
 impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
-    fn emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
         db.emit_producing_nothing();
         rustc_span::fatal_error::FatalError
     }
 }
 
-/// In general, the `DiagnosticBuilder` uses deref to allow access to
-/// the fields and methods of the embedded `diagnostic` in a
-/// transparent way. *However,* many of the methods are intended to
-/// be used in a chained way, and hence ought to return `self`. In
-/// that case, we can't just naively forward to the method on the
-/// `diagnostic`, because the return type would be a `&Diagnostic`
-/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
-/// it easy to declare such methods on the builder.
+/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and
+/// methods of the embedded `Diagnostic`. However, that doesn't allow method
+/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines
+/// two builder methods at that level, both of which wrap the equivalent method
+/// in `Diagnostic`.
+/// - A `&mut self -> &mut Self` method, with the same name as the underlying
+///   `Diagnostic` method. It is mostly to modify existing diagnostics, either
+///   in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion
+///   to make multiple modifications, e.g. `err.code(code).span(span)`.
+/// - A `self -> Self` method, which has a `with_` prefix added.
+///   It is mostly used in a chained fashion when producing a new diagnostic,
+///   e.g. `let err = struct_err(msg).with_code(code)`, or when emitting a new
+///   diagnostic , e.g. `struct_err(msg).with_code(code).emit()`.
+///
+/// Although the latter method can be used to modify an existing diagnostic,
+/// e.g. `err = err.with_code(code)`, this should be avoided because the former
+/// method gives shorter code, e.g. `err.code(code)`.
 macro_rules! forward {
-    // Forward pattern for &mut self -> &mut Self
     (
-        $(#[$attrs:meta])*
-        pub fn $n:ident(&mut self, $($name:ident: $ty:ty),* $(,)?) -> &mut Self
+        ($f:ident, $with_f:ident)($($name:ident: $ty:ty),* $(,)?)
     ) => {
-        $(#[$attrs])*
-        #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
-        pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
-            self.diagnostic.$n($($name),*);
+        #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
+        pub fn $f(&mut self, $($name: $ty),*) -> &mut Self {
+            self.diag.as_mut().unwrap().$f($($name),*);
+            self
+        }
+        #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
+        pub fn $with_f(mut self, $($name: $ty),*) -> Self {
+            self.diag.as_mut().unwrap().$f($($name),*);
             self
         }
     };
@@ -222,13 +202,13 @@ impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
     type Target = Diagnostic;
 
     fn deref(&self) -> &Diagnostic {
-        &self.diagnostic
+        self.diag.as_ref().unwrap()
     }
 }
 
 impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
     fn deref_mut(&mut self) -> &mut Diagnostic {
-        &mut self.diagnostic
+        self.diag.as_mut().unwrap()
     }
 }
 
@@ -242,20 +222,14 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Creates a new `DiagnosticBuilder` with an already constructed
     /// diagnostic.
     #[track_caller]
-    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diagnostic: Diagnostic) -> Self {
+    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self {
         debug!("Created new diagnostic");
-        Self {
-            state: DiagnosticBuilderState::Emittable(dcx),
-            diagnostic: Box::new(diagnostic),
-            _marker: PhantomData,
-        }
+        Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
     }
 
-    /// Emit the diagnostic. Does not consume `self`, which may be surprising,
-    /// but there are various places that rely on continuing to use `self`
-    /// after calling `emit`.
+    /// Emit and consume the diagnostic.
     #[track_caller]
-    pub fn emit(&mut self) -> G::EmitResult {
+    pub fn emit(self) -> G::EmitResult {
         G::emit_producing_guarantee(self)
     }
 
@@ -264,21 +238,17 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     ///
     /// See `emit` and `delay_as_bug` for details.
     #[track_caller]
-    pub fn emit_unless(&mut self, delay: bool) -> G::EmitResult {
+    pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
         if delay {
             self.downgrade_to_delayed_bug();
         }
         self.emit()
     }
 
-    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
+    /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or
     /// cancelled or it will panic when dropped).
-    ///
-    /// This method takes `self` by-value to disallow calling `.emit()` on it,
-    /// which may be expected to *guarantee* the emission of an error, either
-    /// at the time of the call, or through a prior `.emit()` call.
     pub fn cancel(mut self) {
-        self.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+        self.diag = None;
         drop(self);
     }
 
@@ -294,44 +264,20 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     }
 
     /// Converts the builder to a `Diagnostic` for later emission,
-    /// unless dcx has disabled such buffering, or `.emit()` was called.
-    pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
-        let dcx = match self.state {
-            // No `.emit()` calls, the `&DiagCtxt` is still available.
-            DiagnosticBuilderState::Emittable(dcx) => dcx,
-            // `.emit()` was previously called, nothing we can do.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
-                return None;
-            }
-        };
-
-        if dcx.inner.lock().flags.dont_buffer_diagnostics
-            || dcx.inner.lock().flags.treat_err_as_bug.is_some()
-        {
+    /// unless dcx has disabled such buffering.
+    fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a DiagCtxt)> {
+        if self.dcx.inner.lock().flags.treat_err_as_bug.is_some() {
             self.emit();
             return None;
         }
 
-        // Take the `Diagnostic` by replacing it with a dummy.
-        let dummy = Diagnostic::new(Level::Allow, DiagnosticMessage::from(""));
-        let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
-
-        // Disable the ICE on `Drop`.
-        self.cancel();
+        let diag = self.take_diag();
 
         // Logging here is useful to help track down where in logs an error was
         // actually emitted.
-        debug!("buffer: diagnostic={:?}", diagnostic);
+        debug!("buffer: diag={:?}", diag);
 
-        Some((diagnostic, dcx))
-    }
-
-    /// Retrieves the [`DiagCtxt`] if available
-    pub fn dcx(&self) -> Option<&DiagCtxt> {
-        match self.state {
-            DiagnosticBuilderState::Emittable(dcx) => Some(dcx),
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => None,
-        }
+        Some((diag, self.dcx))
     }
 
     /// Buffers the diagnostic for later emission,
@@ -351,207 +297,173 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
     #[track_caller]
-    pub fn delay_as_bug(&mut self) -> G::EmitResult {
+    pub fn delay_as_bug(mut self) -> G::EmitResult {
         self.downgrade_to_delayed_bug();
         self.emit()
     }
 
-    forward!(
-        #[track_caller]
-        pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self
-    );
-
-    forward!(
-    /// Appends a labeled span to the diagnostic.
-    ///
-    /// Labels are used to convey additional context for the diagnostic's primary span. They will
-    /// be shown together with the original diagnostic's span, *not* with spans added by
-    /// `span_note`, `span_help`, etc. Therefore, if the primary span is not displayable (because
-    /// the span is `DUMMY_SP` or the source code isn't found), labels will not be displayed
-    /// either.
-    ///
-    /// Implementation-wise, the label span is pushed onto the [`MultiSpan`] that was created when
-    /// the diagnostic was constructed. However, the label span is *not* considered a
-    /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
-    /// primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self);
-
-    forward!(
-    /// Labels all the given spans with the provided label.
-    /// See [`Diagnostic::span_label()`] for more information.
-    pub fn span_labels(
-        &mut self,
+    forward!((span_label, with_span_label)(
+        span: Span,
+        label: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((span_labels, with_span_labels)(
         spans: impl IntoIterator<Item = Span>,
         label: &str,
-    ) -> &mut Self);
-
-    forward!(pub fn note_expected_found(
-        &mut self,
+    ));
+    forward!((note_expected_found, with_note_expected_found)(
         expected_label: &dyn fmt::Display,
         expected: DiagnosticStyledString,
         found_label: &dyn fmt::Display,
         found: DiagnosticStyledString,
-    ) -> &mut Self);
-
-    forward!(pub fn note_expected_found_extra(
-        &mut self,
+    ));
+    forward!((note_expected_found_extra, with_note_expected_found_extra)(
         expected_label: &dyn fmt::Display,
         expected: DiagnosticStyledString,
         found_label: &dyn fmt::Display,
         found: DiagnosticStyledString,
         expected_extra: &dyn fmt::Display,
         found_extra: &dyn fmt::Display,
-    ) -> &mut Self);
-
-    forward!(pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
-    forward!(pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
-    forward!(pub fn span_note(
-        &mut self,
+    ));
+    forward!((note, with_note)(
+        msg: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((note_once, with_note_once)(
+        msg: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((span_note, with_span_note)(
         sp: impl Into<MultiSpan>,
         msg: impl Into<SubdiagnosticMessage>,
-    ) -> &mut Self);
-    forward!(pub fn span_note_once(
-        &mut self,
+    ));
+    forward!((span_note_once, with_span_note_once)(
         sp: impl Into<MultiSpan>,
         msg: impl Into<SubdiagnosticMessage>,
-    ) -> &mut Self);
-    forward!(pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
-    forward!(pub fn span_warn(
-        &mut self,
+    ));
+    forward!((warn, with_warn)(
+        msg: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((span_warn, with_span_warn)(
         sp: impl Into<MultiSpan>,
         msg: impl Into<SubdiagnosticMessage>,
-    ) -> &mut Self);
-    forward!(pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
-    forward!(pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self);
-    forward!(pub fn span_help(
-        &mut self,
+    ));
+    forward!((help, with_help)(
+        msg: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((help_once, with_help_once)(
+        msg: impl Into<SubdiagnosticMessage>,
+    ));
+    forward!((span_help, with_span_help_once)(
         sp: impl Into<MultiSpan>,
         msg: impl Into<SubdiagnosticMessage>,
-    ) -> &mut Self);
-    forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
-
-    forward!(pub fn disable_suggestions(&mut self,) -> &mut Self);
-
-    forward!(pub fn multipart_suggestion(
-        &mut self,
+    ));
+    forward!((multipart_suggestion, with_multipart_suggestion)(
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn multipart_suggestion_verbose(
-        &mut self,
+    ));
+    forward!((multipart_suggestion_verbose, with_multipart_suggestion_verbose)(
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn tool_only_multipart_suggestion(
-        &mut self,
+    ));
+    forward!((tool_only_multipart_suggestion, with_tool_only_multipart_suggestion)(
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: Vec<(Span, String)>,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn span_suggestion(
-        &mut self,
+    ));
+    forward!((span_suggestion, with_span_suggestion)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn span_suggestions(
-        &mut self,
+    ));
+    forward!((span_suggestions, with_span_suggestions)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl IntoIterator<Item = String>,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn multipart_suggestions(
-        &mut self,
+    ));
+    forward!((multipart_suggestions, with_multipart_suggestions)(
         msg: impl Into<SubdiagnosticMessage>,
         suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn span_suggestion_short(
-        &mut self,
+    ));
+    forward!((span_suggestion_short, with_span_suggestion_short)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn span_suggestion_verbose(
-        &mut self,
+    ));
+    forward!((span_suggestion_verbose, with_span_suggestion_verbose)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn span_suggestion_hidden(
-        &mut self,
+    ));
+    forward!((span_suggestion_hidden, with_span_suggestion_hidden)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self);
-    forward!(pub fn tool_only_span_suggestion(
-        &mut self,
+    ));
+    forward!((tool_only_span_suggestion, with_tool_only_span_suggestion)(
         sp: Span,
         msg: impl Into<SubdiagnosticMessage>,
         suggestion: impl ToString,
         applicability: Applicability,
-    ) -> &mut Self);
-
-    forward!(pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self);
-    forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self);
-    forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
-    forward!(pub fn set_arg(
-        &mut self,
-        name: impl Into<Cow<'static, str>>,
-        arg: impl IntoDiagnosticArg,
-    ) -> &mut Self);
-
-    forward!(pub fn subdiagnostic(
-        &mut self,
-        subdiagnostic: impl crate::AddToDiagnostic
-    ) -> &mut Self);
+    ));
+    forward!((primary_message, with_primary_message)(
+        msg: impl Into<DiagnosticMessage>,
+    ));
+    forward!((span, with_span)(
+        sp: impl Into<MultiSpan>,
+    ));
+    forward!((code, with_code)(
+        s: DiagnosticId,
+    ));
+    forward!((arg, with_arg)(
+        name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
+    ));
+    forward!((subdiagnostic, with_subdiagnostic)(
+        subdiagnostic: impl crate::AddToDiagnostic,
+    ));
+    forward!((eager_subdiagnostic, with_eager_subdiagnostic)(
+        dcx: &DiagCtxt,
+        subdiagnostic: impl crate::AddToDiagnostic,
+    ));
 }
 
 impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.diagnostic.fmt(f)
+        self.diag.fmt(f)
     }
 }
 
-/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
-/// or we emit a bug.
+/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted,
+/// cancelled, etc.) or we emit a bug.
 impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
     fn drop(&mut self) {
-        match self.state {
-            // No `.emit()` or `.cancel()` calls.
-            DiagnosticBuilderState::Emittable(dcx) => {
-                if !panicking() {
-                    dcx.emit_diagnostic(Diagnostic::new(
-                        Level::Bug,
-                        DiagnosticMessage::from(
-                            "the following error was constructed but not emitted",
-                        ),
-                    ));
-                    dcx.emit_diagnostic_without_consuming(&mut self.diagnostic);
-                    panic!("error was constructed but not emitted");
-                }
+        match self.diag.take() {
+            Some(diag) if !panicking() => {
+                self.dcx.emit_diagnostic(Diagnostic::new(
+                    Level::Bug,
+                    DiagnosticMessage::from("the following error was constructed but not emitted"),
+                ));
+                self.dcx.emit_diagnostic(*diag);
+                panic!("error was constructed but not emitted");
             }
-            // `.emit()` was previously called, or maybe we're during `.cancel()`.
-            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+            _ => {}
         }
     }
 }
 
 #[macro_export]
-macro_rules! struct_span_err {
+macro_rules! struct_span_code_err {
     ($dcx:expr, $span:expr, $code:ident, $($message:tt)*) => ({
-        $dcx.struct_span_err_with_code(
+        $dcx.struct_span_err(
             $span,
             format!($($message)*),
-            $crate::error_code!($code),
         )
+        .with_code($crate::error_code!($code))
     })
 }
 
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 29cb304e8b5..39252dea283 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -249,60 +249,43 @@ impl<Id> IntoDiagnosticArg for hir::def::Res<Id> {
 
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TargetDataLayoutErrors<'_> {
     fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
-        let mut diag;
         match self {
             TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
-                diag =
-                    DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space);
-                diag.set_arg("addr_space", addr_space);
-                diag.set_arg("cause", cause);
-                diag.set_arg("err", err);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_address_space)
+                    .with_arg("addr_space", addr_space)
+                    .with_arg("cause", cause)
+                    .with_arg("err", err)
             }
             TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => {
-                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits);
-                diag.set_arg("kind", kind);
-                diag.set_arg("bit", bit);
-                diag.set_arg("cause", cause);
-                diag.set_arg("err", err);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits)
+                    .with_arg("kind", kind)
+                    .with_arg("bit", bit)
+                    .with_arg("cause", cause)
+                    .with_arg("err", err)
             }
             TargetDataLayoutErrors::MissingAlignment { cause } => {
-                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment);
-                diag.set_arg("cause", cause);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_missing_alignment)
+                    .with_arg("cause", cause)
             }
             TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
-                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment);
-                diag.set_arg("cause", cause);
-                diag.set_arg("err_kind", err.diag_ident());
-                diag.set_arg("align", err.align());
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_alignment)
+                    .with_arg("cause", cause)
+                    .with_arg("err_kind", err.diag_ident())
+                    .with_arg("align", err.align())
             }
             TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
-                diag = DiagnosticBuilder::new(
-                    dcx,
-                    level,
-                    fluent::errors_target_inconsistent_architecture,
-                );
-                diag.set_arg("dl", dl);
-                diag.set_arg("target", target);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_architecture)
+                    .with_arg("dl", dl)
+                    .with_arg("target", target)
             }
             TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => {
-                diag = DiagnosticBuilder::new(
-                    dcx,
-                    level,
-                    fluent::errors_target_inconsistent_pointer_width,
-                );
-                diag.set_arg("pointer_size", pointer_size);
-                diag.set_arg("target", target);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_inconsistent_pointer_width)
+                    .with_arg("pointer_size", pointer_size)
+                    .with_arg("target", target)
             }
             TargetDataLayoutErrors::InvalidBitsSize { err } => {
-                diag = DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size);
-                diag.set_arg("err", err);
-                diag
+                DiagnosticBuilder::new(dcx, level, fluent::errors_target_invalid_bits_size)
+                    .with_arg("err", err)
             }
         }
     }
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 546159c9d13..987832e6937 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -61,13 +61,13 @@ impl HumanReadableErrorType {
         self,
         mut dst: Box<dyn WriteColor + Send>,
         fallback_bundle: LazyFallbackBundle,
-    ) -> EmitterWriter {
+    ) -> HumanEmitter {
         let (short, color_config) = self.unzip();
         let color = color_config.suggests_using_colors();
         if !dst.supports_color() && color {
             dst = Box::new(Ansi::new(dst));
         }
-        EmitterWriter::new(dst, fallback_bundle).short_message(short)
+        HumanEmitter::new(dst, fallback_bundle).short_message(short)
     }
 }
 
@@ -196,13 +196,15 @@ pub trait Emitter: Translate {
     fn emit_diagnostic(&mut self, diag: &Diagnostic);
 
     /// Emit a notification that an artifact has been output.
-    /// This is currently only supported for the JSON format,
-    /// other formats can, and will, simply ignore it.
+    /// Currently only supported for the JSON format.
     fn emit_artifact_notification(&mut self, _path: &Path, _artifact_type: &str) {}
 
+    /// Emit a report about future breakage.
+    /// Currently only supported for the JSON format.
     fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
 
-    /// Emit list of unused externs
+    /// Emit list of unused externs.
+    /// Currently only supported for the JSON format.
     fn emit_unused_externs(
         &mut self,
         _lint_level: rustc_lint_defs::Level,
@@ -501,7 +503,7 @@ pub trait Emitter: Translate {
     }
 }
 
-impl Translate for EmitterWriter {
+impl Translate for HumanEmitter {
     fn fluent_bundle(&self) -> Option<&Lrc<FluentBundle>> {
         self.fluent_bundle.as_ref()
     }
@@ -511,7 +513,7 @@ impl Translate for EmitterWriter {
     }
 }
 
-impl Emitter for EmitterWriter {
+impl Emitter for HumanEmitter {
     fn source_map(&self) -> Option<&Lrc<SourceMap>> {
         self.sm.as_ref()
     }
@@ -622,7 +624,7 @@ impl ColorConfig {
 
 /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short`
 #[derive(Setters)]
-pub struct EmitterWriter {
+pub struct HumanEmitter {
     #[setters(skip)]
     dst: IntoDynSyncSend<Destination>,
     sm: Option<Lrc<SourceMap>>,
@@ -647,14 +649,14 @@ pub struct FileWithAnnotatedLines {
     multiline_depth: usize,
 }
 
-impl EmitterWriter {
-    pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter {
+impl HumanEmitter {
+    pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
         let dst = from_stderr(color_config);
         Self::create(dst, fallback_bundle)
     }
 
-    fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter {
-        EmitterWriter {
+    fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> HumanEmitter {
+        HumanEmitter {
             dst: IntoDynSyncSend(dst),
             sm: None,
             fluent_bundle: None,
@@ -673,7 +675,7 @@ impl EmitterWriter {
     pub fn new(
         dst: Box<dyn WriteColor + Send>,
         fallback_bundle: LazyFallbackBundle,
-    ) -> EmitterWriter {
+    ) -> HumanEmitter {
         Self::create(dst, fallback_bundle)
     }
 
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index 52fcb50e9fb..87bf9c23456 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -198,7 +198,7 @@ impl Emitter for JsonEmitter {
             .into_iter()
             .map(|mut diag| {
                 if diag.level == crate::Level::Allow {
-                    diag.level = crate::Level::Warning(None);
+                    diag.level = crate::Level::Warning;
                 }
                 FutureBreakageItem {
                     diagnostic: EmitTyped::Diagnostic(Diagnostic::from_errors_diagnostic(
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index e9507dcfed7..8fb539fc358 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -7,9 +7,11 @@
 #![feature(rustdoc_internals)]
 #![feature(array_windows)]
 #![feature(associated_type_defaults)]
+#![feature(box_into_inner)]
 #![feature(extract_if)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
+#![feature(negative_impls)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(yeet_expr)]
@@ -53,7 +55,7 @@ pub use snippet::Style;
 pub use termcolor::{Color, ColorSpec, WriteColor};
 
 use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
-use emitter::{is_case_difference, DynEmitter, Emitter, EmitterWriter};
+use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter};
 use registry::Registry;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{Hash128, StableHasher};
@@ -418,6 +420,7 @@ pub struct DiagCtxt {
 /// as well as inconsistent state observation.
 struct DiagCtxtInner {
     flags: DiagCtxtFlags,
+
     /// The number of lint errors that have been emitted.
     lint_err_count: usize,
     /// The number of errors that have been emitted, including duplicates.
@@ -425,8 +428,13 @@ struct DiagCtxtInner {
     /// This is not necessarily the count that's reported to the user once
     /// compilation ends.
     err_count: usize,
-    warn_count: usize,
     deduplicated_err_count: usize,
+    /// The warning count, used for a recap upon finishing
+    deduplicated_warn_count: usize,
+    /// Has this diagnostic context printed any diagnostics? (I.e. has
+    /// `self.emitter.emit_diagnostic()` been called?
+    has_printed: bool,
+
     emitter: Box<DynEmitter>,
     span_delayed_bugs: Vec<DelayedDiagnostic>,
     good_path_delayed_bugs: Vec<DelayedDiagnostic>,
@@ -453,9 +461,6 @@ struct DiagCtxtInner {
     /// When `.abort_if_errors()` is called, these are also emitted.
     stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
 
-    /// The warning count, used for a recap upon finishing
-    deduplicated_warn_count: usize,
-
     future_breakage_diagnostics: Vec<Diagnostic>,
 
     /// The [`Self::unstable_expect_diagnostics`] should be empty when this struct is
@@ -507,11 +512,11 @@ pub enum StashKey {
     Cycle,
 }
 
-fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {
-    (*f)(d)
+fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
+    (*f)(diag)
 }
 
-pub static TRACK_DIAGNOSTICS: AtomicRef<fn(&mut Diagnostic, &mut dyn FnMut(&mut Diagnostic))> =
+pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
     AtomicRef::new(&(default_track_diagnostic as _));
 
 #[derive(Copy, Clone, Default)]
@@ -522,12 +527,6 @@ pub struct DiagCtxtFlags {
     /// If Some, the Nth error-level diagnostic is upgraded to bug-level.
     /// (rustc: see `-Z treat-err-as-bug`)
     pub treat_err_as_bug: Option<NonZeroUsize>,
-    /// If true, immediately emit diagnostics that would otherwise be buffered.
-    /// (rustc: see `-Z dont-buffer-diagnostics` and `-Z treat-err-as-bug`)
-    pub dont_buffer_diagnostics: bool,
-    /// If true, immediately print bugs registered with `span_delayed_bug`.
-    /// (rustc: see `-Z report-delayed-bugs`)
-    pub report_delayed_bugs: bool,
     /// Show macro backtraces.
     /// (rustc: see `-Z macro-backtrace`)
     pub macro_backtrace: bool,
@@ -551,8 +550,7 @@ impl Drop for DiagCtxtInner {
         // instead of "require some error happened". Sadly that isn't ideal, as
         // lints can be `#[allow]`'d, potentially leading to this triggering.
         // Also, "good path" should be replaced with a better naming.
-        let has_any_message = self.err_count > 0 || self.lint_err_count > 0 || self.warn_count > 0;
-        if !has_any_message && !self.suppressed_expected_diag && !std::thread::panicking() {
+        if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
             let bugs = std::mem::replace(&mut self.good_path_delayed_bugs, Vec::new());
             self.flush_delayed(
                 bugs,
@@ -574,7 +572,7 @@ impl DiagCtxt {
         sm: Option<Lrc<SourceMap>>,
         fallback_bundle: LazyFallbackBundle,
     ) -> Self {
-        let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm));
+        let emitter = Box::new(HumanEmitter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm));
         Self::with_emitter(emitter)
     }
     pub fn disable_warnings(mut self) -> Self {
@@ -598,9 +596,9 @@ impl DiagCtxt {
                 flags: DiagCtxtFlags { can_emit_warnings: true, ..Default::default() },
                 lint_err_count: 0,
                 err_count: 0,
-                warn_count: 0,
                 deduplicated_err_count: 0,
                 deduplicated_warn_count: 0,
+                has_printed: false,
                 emitter,
                 span_delayed_bugs: Vec::new(),
                 good_path_delayed_bugs: Vec::new(),
@@ -651,10 +649,11 @@ impl DiagCtxt {
     /// the overall count of emitted error diagnostics.
     pub fn reset_err_count(&self) {
         let mut inner = self.inner.borrow_mut();
+        inner.lint_err_count = 0;
         inner.err_count = 0;
-        inner.warn_count = 0;
         inner.deduplicated_err_count = 0;
         inner.deduplicated_warn_count = 0;
+        inner.has_printed = false;
 
         // actually free the underlying memory (which `clear` would not do)
         inner.span_delayed_bugs = Default::default();
@@ -673,16 +672,11 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
 
         if diag.is_error() {
-            if matches!(diag.level, Error { lint: true }) {
+            if diag.is_lint {
                 inner.lint_err_count += 1;
             } else {
                 inner.err_count += 1;
             }
-        } else {
-            // Warnings are only automatically flushed if they're forced.
-            if diag.is_force_warn() {
-                inner.warn_count += 1;
-            }
         }
 
         // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
@@ -697,15 +691,11 @@ impl DiagCtxt {
         let key = (span.with_parent(None), key);
         let diag = inner.stashed_diagnostics.remove(&key)?;
         if diag.is_error() {
-            if matches!(diag.level, Error { lint: true }) {
+            if diag.is_lint {
                 inner.lint_err_count -= 1;
             } else {
                 inner.err_count -= 1;
             }
-        } else {
-            if diag.is_force_warn() {
-                inner.warn_count -= 1;
-            }
         }
         Some(DiagnosticBuilder::new_diagnostic(self, diag))
     }
@@ -731,24 +721,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_warn(msg);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
-    /// Also include a code.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_warn_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_span_warn(span, msg);
-        result.code(code);
-        result
+        self.struct_warn(msg).with_span(span)
     }
 
     /// Construct a builder at the `Warning` level with the `msg`.
@@ -759,7 +732,7 @@ impl DiagCtxt {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Warning(None), msg)
+        DiagnosticBuilder::new(self, Warning, msg)
     }
 
     /// Construct a builder at the `Allow` level with the `msg`.
@@ -788,23 +761,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_> {
-        let mut result = self.struct_err(msg);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Error` level at the given `span`, with the `msg`, and `code`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_err_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
-        let mut result = self.struct_span_err(span, msg);
-        result.code(code);
-        result
+        self.struct_err(msg).with_span(span)
     }
 
     /// Construct a builder at the `Error` level with the `msg`.
@@ -812,33 +769,7 @@ impl DiagCtxt {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_err(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_> {
-        DiagnosticBuilder::new(self, Error { lint: false }, msg)
-    }
-
-    /// Construct a builder at the `Error` level with the `msg` and the `code`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_err_with_code(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
-        let mut result = self.struct_err(msg);
-        result.code(code);
-        result
-    }
-
-    /// Construct a builder at the `Warn` level with the `msg` and the `code`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_warn_with_code(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, ()> {
-        let mut result = self.struct_warn(msg);
-        result.code(code);
-        result
+        DiagnosticBuilder::new(self, Error, msg)
     }
 
     /// Construct a builder at the `Fatal` level at the given `span` and with the `msg`.
@@ -849,23 +780,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, FatalAbort> {
-        let mut result = self.struct_fatal(msg);
-        result.set_span(span);
-        result
-    }
-
-    /// Construct a builder at the `Fatal` level at the given `span`, with the `msg`, and `code`.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_span_fatal_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_, FatalAbort> {
-        let mut result = self.struct_span_fatal(span, msg);
-        result.code(code);
-        result
+        self.struct_fatal(msg).with_span(span)
     }
 
     /// Construct a builder at the `Fatal` level with the `msg`.
@@ -878,16 +793,6 @@ impl DiagCtxt {
         DiagnosticBuilder::new(self, Fatal, msg)
     }
 
-    /// Construct a builder at the `Fatal` level with the `msg`, that doesn't abort.
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn struct_almost_fatal(
-        &self,
-        msg: impl Into<DiagnosticMessage>,
-    ) -> DiagnosticBuilder<'_, FatalError> {
-        DiagnosticBuilder::new(self, Fatal, msg)
-    }
-
     /// Construct a builder at the `Help` level with the `msg`.
     #[rustc_lint_diagnostics]
     pub fn struct_help(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
@@ -916,9 +821,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, BugAbort> {
-        let mut result = self.struct_bug(msg);
-        result.set_span(span);
-        result
+        self.struct_bug(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
@@ -929,17 +832,6 @@ impl DiagCtxt {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn span_fatal_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> ! {
-        self.struct_span_fatal_with_code(span, msg, code).emit()
-    }
-
-    #[rustc_lint_diagnostics]
-    #[track_caller]
     pub fn span_err(
         &self,
         span: impl Into<MultiSpan>,
@@ -950,32 +842,10 @@ impl DiagCtxt {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn span_err_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) -> ErrorGuaranteed {
-        self.struct_span_err_with_code(span, msg, code).emit()
-    }
-
-    #[rustc_lint_diagnostics]
-    #[track_caller]
     pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) {
         self.struct_span_warn(span, msg).emit()
     }
 
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn span_warn_with_code(
-        &self,
-        span: impl Into<MultiSpan>,
-        msg: impl Into<DiagnosticMessage>,
-        code: DiagnosticId,
-    ) {
-        self.struct_span_warn_with_code(span, msg, code).emit()
-    }
-
     pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagnosticMessage>) -> ! {
         self.struct_span_bug(span, msg).emit()
     }
@@ -989,10 +859,16 @@ impl DiagCtxt {
     /// For example, it can be used to create an [`ErrorGuaranteed`]
     /// (but you should prefer threading through the [`ErrorGuaranteed`] from an error emission
     /// directly).
-    ///
-    /// If no span is available, use [`DUMMY_SP`].
-    ///
-    /// [`DUMMY_SP`]: rustc_span::DUMMY_SP
+    #[track_caller]
+    pub fn delayed_bug(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
+        let treat_next_err_as_bug = self.inner.borrow().treat_next_err_as_bug();
+        if treat_next_err_as_bug {
+            self.bug(msg);
+        }
+        DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).emit()
+    }
+
+    /// Like `delayed_bug`, but takes an additional span.
     ///
     /// Note: this function used to be called `delay_span_bug`. It was renamed
     /// to match similar functions like `span_err`, `span_warn`, etc.
@@ -1004,23 +880,16 @@ impl DiagCtxt {
     ) -> ErrorGuaranteed {
         let treat_next_err_as_bug = self.inner.borrow().treat_next_err_as_bug();
         if treat_next_err_as_bug {
-            // FIXME: don't abort here if report_delayed_bugs is off
             self.span_bug(sp, msg);
         }
-        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
-        diagnostic.set_span(sp);
-        self.emit_diagnostic(diagnostic).unwrap()
+        DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
     }
 
     // FIXME(eddyb) note the comment inside `impl Drop for DiagCtxtInner`, that's
     // where the explanation of what "good path" is (also, it should be renamed).
     pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
         let mut inner = self.inner.borrow_mut();
-
-        let mut diagnostic = Diagnostic::new(DelayedBug, msg);
-        if inner.flags.report_delayed_bugs {
-            inner.emit_diagnostic_without_consuming(&mut diagnostic);
-        }
+        let diagnostic = Diagnostic::new(DelayedBug, msg);
         let backtrace = std::backtrace::Backtrace::capture();
         inner.good_path_delayed_bugs.push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
     }
@@ -1038,9 +907,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ()> {
-        let mut db = DiagnosticBuilder::new(self, Note, msg);
-        db.set_span(span);
-        db
+        DiagnosticBuilder::new(self, Note, msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
@@ -1132,7 +999,7 @@ impl DiagCtxt {
             (0, 0) => return,
             (0, _) => inner
                 .emitter
-                .emit_diagnostic(&Diagnostic::new(Warning(None), DiagnosticMessage::Str(warnings))),
+                .emit_diagnostic(&Diagnostic::new(Warning, DiagnosticMessage::Str(warnings))),
             (_, 0) => {
                 inner.emit_diagnostic(Diagnostic::new(Fatal, errors));
             }
@@ -1202,17 +1069,8 @@ impl DiagCtxt {
         self.inner.borrow_mut().emitter.emit_diagnostic(&db);
     }
 
-    pub fn emit_diagnostic(&self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
-        self.emit_diagnostic_without_consuming(&mut diagnostic)
-    }
-
-    // It's unfortunate this exists. `emit_diagnostic` is preferred, because it
-    // consumes the diagnostic, thus ensuring it is emitted just once.
-    pub(crate) fn emit_diagnostic_without_consuming(
-        &self,
-        diagnostic: &mut Diagnostic,
-    ) -> Option<ErrorGuaranteed> {
-        self.inner.borrow_mut().emit_diagnostic_without_consuming(diagnostic)
+    pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
+        self.inner.borrow_mut().emit_diagnostic(diagnostic)
     }
 
     #[track_caller]
@@ -1222,20 +1080,20 @@ impl DiagCtxt {
 
     #[track_caller]
     pub fn create_err<'a>(&'a self, err: impl IntoDiagnostic<'a>) -> DiagnosticBuilder<'a> {
-        err.into_diagnostic(self, Error { lint: false })
+        err.into_diagnostic(self, Error)
     }
 
     #[track_caller]
-    pub fn create_warning<'a>(
+    pub fn create_warn<'a>(
         &'a self,
         warning: impl IntoDiagnostic<'a, ()>,
     ) -> DiagnosticBuilder<'a, ()> {
-        warning.into_diagnostic(self, Warning(None))
+        warning.into_diagnostic(self, Warning)
     }
 
     #[track_caller]
-    pub fn emit_warning<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
-        self.create_warning(warning).emit()
+    pub fn emit_warn<'a>(&'a self, warning: impl IntoDiagnostic<'a, ()>) {
+        self.create_warn(warning).emit()
     }
 
     #[track_caller]
@@ -1367,7 +1225,7 @@ impl DiagCtxt {
 // Note: we prefer implementing operations on `DiagCtxt`, rather than
 // `DiagCtxtInner`, whenever possible. This minimizes functions where
 // `DiagCtxt::foo()` just borrows `inner` and forwards a call to
-// `HanderInner::foo`.
+// `DiagCtxtInner::foo`.
 impl DiagCtxtInner {
     /// Emit all stashed diagnostics.
     fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
@@ -1377,21 +1235,17 @@ impl DiagCtxtInner {
         for diag in diags {
             // Decrement the count tracking the stash; emitting will increment it.
             if diag.is_error() {
-                if matches!(diag.level, Error { lint: true }) {
+                if diag.is_lint {
                     self.lint_err_count -= 1;
                 } else {
                     self.err_count -= 1;
                 }
             } else {
-                if diag.is_force_warn() {
-                    self.warn_count -= 1;
-                } else {
-                    // Unless they're forced, don't flush stashed warnings when
-                    // there are errors, to avoid causing warning overload. The
-                    // stash would've been stolen already if it were important.
-                    if has_errors {
-                        continue;
-                    }
+                // Unless they're forced, don't flush stashed warnings when
+                // there are errors, to avoid causing warning overload. The
+                // stash would've been stolen already if it were important.
+                if !diag.is_force_warn() && has_errors {
+                    continue;
                 }
             }
             let reported_this = self.emit_diagnostic(diag);
@@ -1401,14 +1255,7 @@ impl DiagCtxtInner {
     }
 
     fn emit_diagnostic(&mut self, mut diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
-        self.emit_diagnostic_without_consuming(&mut diagnostic)
-    }
-
-    fn emit_diagnostic_without_consuming(
-        &mut self,
-        diagnostic: &mut Diagnostic,
-    ) -> Option<ErrorGuaranteed> {
-        if matches!(diagnostic.level, Error { .. } | Fatal) && self.treat_err_as_bug() {
+        if matches!(diagnostic.level, Error | Fatal) && self.treat_err_as_bug() {
             diagnostic.level = Bug;
         }
 
@@ -1430,10 +1277,8 @@ impl DiagCtxtInner {
             self.span_delayed_bugs
                 .push(DelayedDiagnostic::with_backtrace(diagnostic.clone(), backtrace));
 
-            if !self.flags.report_delayed_bugs {
-                #[allow(deprecated)]
-                return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
-            }
+            #[allow(deprecated)]
+            return Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
         }
 
         if diagnostic.has_future_breakage() {
@@ -1449,23 +1294,20 @@ impl DiagCtxtInner {
             self.fulfilled_expectations.insert(expectation_id.normalize());
         }
 
-        if matches!(diagnostic.level, Warning(_))
-            && !self.flags.can_emit_warnings
-            && !diagnostic.is_force_warn()
-        {
+        if diagnostic.level == Warning && !self.flags.can_emit_warnings {
             if diagnostic.has_future_breakage() {
-                (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
+                (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
             }
             return None;
         }
 
         if matches!(diagnostic.level, Expect(_) | Allow) {
-            (*TRACK_DIAGNOSTICS)(diagnostic, &mut |_| {});
+            (*TRACK_DIAGNOSTIC)(diagnostic, &mut |_| {});
             return None;
         }
 
         let mut guaranteed = None;
-        (*TRACK_DIAGNOSTICS)(diagnostic, &mut |diagnostic| {
+        (*TRACK_DIAGNOSTIC)(diagnostic, &mut |mut diagnostic| {
             if let Some(ref code) = diagnostic.code {
                 self.emitted_diagnostic_codes.insert(code.clone());
             }
@@ -1501,15 +1343,16 @@ impl DiagCtxtInner {
                     );
                 }
 
-                self.emitter.emit_diagnostic(diagnostic);
+                self.emitter.emit_diagnostic(&diagnostic);
                 if diagnostic.is_error() {
                     self.deduplicated_err_count += 1;
-                } else if let Warning(_) = diagnostic.level {
+                } else if matches!(diagnostic.level, ForceWarning(_) | Warning) {
                     self.deduplicated_warn_count += 1;
                 }
+                self.has_printed = true;
             }
             if diagnostic.is_error() {
-                if matches!(diagnostic.level, Error { lint: true }) {
+                if diagnostic.is_lint {
                     self.bump_lint_err_count();
                 } else {
                     self.bump_err_count();
@@ -1519,8 +1362,6 @@ impl DiagCtxtInner {
                 {
                     guaranteed = Some(ErrorGuaranteed::unchecked_claim_error_was_emitted());
                 }
-            } else {
-                self.bump_warn_count();
             }
         });
 
@@ -1616,10 +1457,6 @@ impl DiagCtxtInner {
         self.panic_if_treat_err_as_bug();
     }
 
-    fn bump_warn_count(&mut self) {
-        self.warn_count += 1;
-    }
-
     fn panic_if_treat_err_as_bug(&self) {
         if self.treat_err_as_bug() {
             match (
@@ -1698,27 +1535,26 @@ pub enum Level {
     /// internal overflows, some file operation errors.
     ///
     /// Its `EmissionGuarantee` is `FatalAbort`, except in the non-aborting "almost fatal" case
-    /// that is occasionaly used, where it is `FatalError`.
+    /// that is occasionally used, where it is `FatalError`.
     Fatal,
 
     /// An error in the code being compiled, which prevents compilation from finishing. This is the
     /// most common case.
     ///
     /// Its `EmissionGuarantee` is `ErrorGuaranteed`.
-    Error {
-        /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is
-        /// called.
-        lint: bool,
-    },
+    Error,
 
-    /// A warning about the code being compiled. Does not prevent compilation from finishing.
+    /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
+    /// from finishing.
     ///
-    /// This [`LintExpectationId`] is used for expected lint diagnostics, which should
-    /// also emit a warning due to the `force-warn` flag. In all other cases this should
-    /// be `None`.
+    /// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this
+    /// should be `None`.
+    ForceWarning(Option<LintExpectationId>),
+
+    /// A warning about the code being compiled. Does not prevent compilation from finishing.
     ///
     /// Its `EmissionGuarantee` is `()`.
-    Warning(Option<LintExpectationId>),
+    Warning,
 
     /// A message giving additional context. Rare, because notes are more commonly attached to other
     /// diagnostics such as errors.
@@ -1768,10 +1604,10 @@ impl Level {
     fn color(self) -> ColorSpec {
         let mut spec = ColorSpec::new();
         match self {
-            Bug | DelayedBug | Fatal | Error { .. } => {
+            Bug | DelayedBug | Fatal | Error => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
-            Warning(_) => {
+            ForceWarning(_) | Warning => {
                 spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
             }
             Note | OnceNote => {
@@ -1789,8 +1625,8 @@ impl Level {
     pub fn to_str(self) -> &'static str {
         match self {
             Bug | DelayedBug => "error: internal compiler error",
-            Fatal | Error { .. } => "error",
-            Warning(_) => "warning",
+            Fatal | Error => "error",
+            ForceWarning(_) | Warning => "warning",
             Note | OnceNote => "note",
             Help | OnceHelp => "help",
             FailureNote => "failure-note",
@@ -1804,7 +1640,7 @@ impl Level {
 
     pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
         match self {
-            Expect(id) | Warning(Some(id)) => Some(*id),
+            Expect(id) | ForceWarning(Some(id)) => Some(*id),
             _ => None,
         }
     }
diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml
index 9189a501aa5..63247f9d051 100644
--- a/compiler/rustc_expand/Cargo.toml
+++ b/compiler/rustc_expand/Cargo.toml
@@ -9,7 +9,6 @@ doctest = false
 
 [dependencies]
 # tidy-alphabetical-start
-crossbeam-channel = "0.5.0"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_passes = { path = "../rustc_ast_passes" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 8b5a22d1914..e87f2306bc7 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1232,7 +1232,7 @@ pub fn expr_to_string(
 ) -> Option<(Symbol, ast::StrStyle)> {
     expr_to_spanned_string(cx, expr, err_msg)
         .map_err(|err| {
-            err.map(|(mut err, _)| {
+            err.map(|(err, _)| {
                 err.emit();
             })
         })
@@ -1254,7 +1254,7 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str
 pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
     match p.parse_expr() {
         Ok(e) => return Some(e),
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
         }
     }
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 2283a3bfc76..d015d779963 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -362,7 +362,7 @@ impl<'a> StripUnconfigured<'a> {
     pub(crate) fn cfg_true(&self, attr: &Attribute) -> (bool, Option<MetaItem>) {
         let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
             Ok(meta_item) => meta_item,
-            Err(mut err) => {
+            Err(err) => {
                 err.emit();
                 return (true, None);
             }
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 676f9f17976..c39a3dce34e 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -735,7 +735,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                                 fragment_kind.expect_from_annotatables(items)
                             }
                         }
-                        Err(mut err) => {
+                        Err(err) => {
                             err.emit();
                             fragment_kind.dummy(span)
                         }
@@ -855,7 +855,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             }
             Err(mut err) => {
                 if err.span.is_dummy() {
-                    err.set_span(span);
+                    err.span(span);
                 }
                 annotate_err_with_kind(&mut err, kind, span);
                 err.emit();
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 2746e888b8d..eec86c36aed 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -180,7 +180,7 @@ impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx,
             }
             Error(err_sp, msg) => {
                 let span = err_sp.substitute_dummy(self.root_span);
-                self.cx.dcx().struct_span_err(span, msg.clone()).emit();
+                self.cx.dcx().span_err(span, msg.clone());
                 self.result = Some(DummyResult::any(span));
             }
             ErrorReported(_) => self.result = Some(DummyResult::any(self.root_span)),
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index b248a1fe349..ac5136539c3 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -679,8 +679,8 @@ impl TtParser {
                         // We use the span of the metavariable declaration to determine any
                         // edition-specific matching behavior for non-terminals.
                         let nt = match parser.to_mut().parse_nonterminal(kind) {
-                            Err(mut err) => {
-                                let guarantee = err.span_label(
+                            Err(err) => {
+                                let guarantee = err.with_span_label(
                                     span,
                                     format!(
                                         "while parsing argument for this `{kind}` macro fragment"
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index e9736d6f2c8..a56c980791a 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -10,7 +10,7 @@ use crate::mbe::transcribe::transcribe;
 
 use rustc_ast as ast;
 use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
-use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
+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};
@@ -213,45 +213,14 @@ fn expand_macro<'cx>(
             let arm_span = rhses[i].span();
 
             // rhs has holes ( `$id` and `$(...)` that need filled)
-            let mut tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
+            let tts = match transcribe(cx, &named_matches, rhs, rhs_span, transparency) {
                 Ok(tts) => tts,
-                Err(mut err) => {
+                Err(err) => {
                     err.emit();
                     return DummyResult::any(arm_span);
                 }
             };
 
-            // Replace all the tokens for the corresponding positions in the macro, to maintain
-            // proper positions in error reporting, while maintaining the macro_backtrace.
-            if tts.len() == rhs.tts.len() {
-                tts = tts.map_enumerated_owned(|i, mut tt| {
-                    let rhs_tt = &rhs.tts[i];
-                    let ctxt = tt.span().ctxt();
-                    match (&mut tt, rhs_tt) {
-                        // preserve the delim spans if able
-                        (
-                            TokenTree::Delimited(target_sp, ..),
-                            mbe::TokenTree::Delimited(source_sp, ..),
-                        ) => {
-                            target_sp.open = source_sp.open.with_ctxt(ctxt);
-                            target_sp.close = source_sp.close.with_ctxt(ctxt);
-                        }
-                        (
-                            TokenTree::Delimited(target_sp, ..),
-                            mbe::TokenTree::MetaVar(source_sp, ..),
-                        ) => {
-                            target_sp.open = source_sp.with_ctxt(ctxt);
-                            target_sp.close = source_sp.with_ctxt(ctxt).shrink_to_hi();
-                        }
-                        _ => {
-                            let sp = rhs_tt.span().with_ctxt(ctxt);
-                            tt.set_span(sp);
-                        }
-                    }
-                    tt
-                });
-            }
-
             if cx.trace_macros() {
                 let msg = format!("to `{}`", pprust::tts_to_string(&tts));
                 trace_macros_note(&mut cx.expansions, sp, msg);
@@ -489,7 +458,7 @@ pub fn compile_declarative_macro(
                 return dummy_syn_ext();
             }
             Error(sp, msg) => {
-                sess.dcx().struct_span_err(sp.substitute_dummy(def.span), msg).emit();
+                sess.dcx().span_err(sp.substitute_dummy(def.span), msg);
                 return dummy_syn_ext();
             }
             ErrorReported(_) => {
diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs
index 445be01bc97..889f43ed203 100644
--- a/compiler/rustc_expand/src/mbe/quoted.rs
+++ b/compiler/rustc_expand/src/mbe/quoted.rs
@@ -72,7 +72,7 @@ pub(super) fn parse(
                                             // `SyntaxContext::root()` from a foreign crate will
                                             // have the edition of that crate (which we manually
                                             // retrieve via the `edition` parameter).
-                                            if span.ctxt().is_root() {
+                                            if !span.from_expansion() {
                                                 edition
                                             } else {
                                                 span.edition()
@@ -86,7 +86,7 @@ pub(super) fn parse(
                                                 );
                                                 sess.dcx
                                                     .struct_span_err(span, msg)
-                                                    .help(VALID_FRAGMENT_NAMES_MSG)
+                                                    .with_help(VALID_FRAGMENT_NAMES_MSG)
                                                     .emit();
                                                 token::NonterminalKind::Ident
                                             },
@@ -175,7 +175,7 @@ fn parse_tree<'a>(
                                 // of a meta-variable expression (e.g. `${count(ident)}`).
                                 // Try to parse the meta-variable expression.
                                 match MetaVarExpr::parse(tts, delim_span.entire(), sess) {
-                                    Err(mut err) => {
+                                    Err(err) => {
                                         err.emit();
                                         // Returns early the same read `$` to avoid spanning
                                         // unrelated diagnostics that could be performed afterwards
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index f2a9875ffd2..434891ebc76 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -4,7 +4,7 @@ use crate::errors::{
     NoSyntaxVarsExprRepeat, VarStillRepeating,
 };
 use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
-use crate::mbe::{self, MetaVarExpr};
+use crate::mbe::{self, KleeneOp, MetaVarExpr};
 use rustc_ast::mut_visit::{self, MutVisitor};
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
@@ -13,19 +13,28 @@ use rustc_errors::DiagnosticBuilder;
 use rustc_errors::{pluralize, PResult};
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
-use rustc_span::Span;
+use rustc_span::{Span, SyntaxContext};
 
 use smallvec::{smallvec, SmallVec};
 use std::mem;
 
 // A Marker adds the given mark to the syntax context.
-struct Marker(LocalExpnId, Transparency);
+struct Marker(LocalExpnId, Transparency, FxHashMap<SyntaxContext, SyntaxContext>);
 
 impl MutVisitor for Marker {
     const VISIT_TOKENS: bool = true;
 
     fn visit_span(&mut self, span: &mut Span) {
-        *span = span.apply_mark(self.0.to_expn_id(), self.1)
+        // `apply_mark` is a relatively expensive operation, both due to taking hygiene lock, and
+        // by itself. All tokens in a macro body typically have the same syntactic context, unless
+        // it's some advanced case with macro-generated macros. So if we cache the marked version
+        // of that context once, we'll typically have a 100% cache hit rate after that.
+        let Marker(expn_id, transparency, ref mut cache) = *self;
+        let data = span.data();
+        let marked_ctxt = *cache
+            .entry(data.ctxt)
+            .or_insert_with(|| data.ctxt.apply_mark(expn_id.to_expn_id(), transparency));
+        *span = data.with_ctxt(marked_ctxt);
     }
 }
 
@@ -42,6 +51,7 @@ enum Frame<'a> {
         tts: &'a [mbe::TokenTree],
         idx: usize,
         sep: Option<Token>,
+        kleene_op: KleeneOp,
     },
 }
 
@@ -122,7 +132,7 @@ pub(super) fn transcribe<'a>(
     // again, and we are done transcribing.
     let mut result: Vec<TokenTree> = Vec::new();
     let mut result_stack = Vec::new();
-    let mut marker = Marker(cx.current_expansion.id, transparency);
+    let mut marker = Marker(cx.current_expansion.id, transparency, Default::default());
 
     loop {
         // Look at the last frame on the stack.
@@ -207,7 +217,7 @@ pub(super) fn transcribe<'a>(
 
                         // Is the repetition empty?
                         if len == 0 {
-                            if seq.kleene.op == mbe::KleeneOp::OneOrMore {
+                            if seq.kleene.op == KleeneOp::OneOrMore {
                                 // FIXME: this really ought to be caught at macro definition
                                 // time... It happens when the Kleene operator in the matcher and
                                 // the body for the same meta-variable do not match.
@@ -227,6 +237,7 @@ pub(super) fn transcribe<'a>(
                                 idx: 0,
                                 sep: seq.separator.clone(),
                                 tts: &delimited.tts,
+                                kleene_op: seq.kleene.op,
                             });
                         }
                     }
@@ -243,7 +254,7 @@ pub(super) fn transcribe<'a>(
                         MatchedTokenTree(tt) => {
                             // `tt`s are emitted into the output stream directly as "raw tokens",
                             // without wrapping them into groups.
-                            result.push(tt.clone());
+                            result.push(maybe_use_metavar_location(cx, &stack, sp, tt));
                         }
                         MatchedNonterminal(nt) => {
                             // Other variables are emitted into the output stream as groups with
@@ -308,6 +319,62 @@ pub(super) fn transcribe<'a>(
     }
 }
 
+/// Usually metavariables `$var` produce interpolated tokens, which have an additional place for
+/// keeping both the original span and the metavariable span. For `tt` metavariables that's not the
+/// case however, and there's no place for keeping a second span. So we try to give the single
+/// produced span a location that would be most useful in practice (the hygiene part of the span
+/// must not be changed).
+///
+/// Different locations are useful for different purposes:
+/// - The original location is useful when we need to report a diagnostic for the original token in
+///   isolation, without combining it with any surrounding tokens. This case occurs, but it is not
+///   very common in practice.
+/// - The metavariable location is useful when we need to somehow combine the token span with spans
+///   of its surrounding tokens. This is the most common way to use token spans.
+///
+/// So this function replaces the original location with the metavariable location in all cases
+/// except these two:
+/// - The metavariable is an element of undelimited sequence `$($tt)*`.
+///   These are typically used for passing larger amounts of code, and tokens in that code usually
+///   combine with each other and not with tokens outside of the sequence.
+/// - The metavariable span comes from a different crate, then we prefer the more local span.
+///
+/// FIXME: Find a way to keep both original and metavariable spans for all tokens without
+/// regressing compilation time too much. Several experiments for adding such spans were made in
+/// the past (PR #95580, #118517, #118671) and all showed some regressions.
+fn maybe_use_metavar_location(
+    cx: &ExtCtxt<'_>,
+    stack: &[Frame<'_>],
+    metavar_span: Span,
+    orig_tt: &TokenTree,
+) -> TokenTree {
+    let undelimited_seq = matches!(
+        stack.last(),
+        Some(Frame::Sequence {
+            tts: [_],
+            sep: None,
+            kleene_op: KleeneOp::ZeroOrMore | KleeneOp::OneOrMore,
+            ..
+        })
+    );
+    if undelimited_seq || cx.source_map().is_imported(metavar_span) {
+        return orig_tt.clone();
+    }
+
+    match orig_tt {
+        TokenTree::Token(Token { kind, span }, spacing) => {
+            let span = metavar_span.with_ctxt(span.ctxt());
+            TokenTree::Token(Token { kind: kind.clone(), span }, *spacing)
+        }
+        TokenTree::Delimited(dspan, dspacing, delimiter, tts) => {
+            let open = metavar_span.shrink_to_lo().with_ctxt(dspan.open.ctxt());
+            let close = metavar_span.shrink_to_hi().with_ctxt(dspan.close.ctxt());
+            let dspan = DelimSpan::from_pair(open, close);
+            TokenTree::Delimited(dspan, *dspacing, *delimiter, tts.clone())
+        }
+    }
+}
+
 /// Lookup the meta-var named `ident` and return the matched token tree from the invocation using
 /// the set of matches `interpolations`.
 ///
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 60647c3350a..e979f9a75d4 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -282,7 +282,7 @@ impl ModError<'_> {
                     secondary_path: secondary_path.display().to_string(),
                 })
             }
-            ModError::ParserError(mut err) => err.emit(),
+            ModError::ParserError(err) => err.emit(),
         }
     }
 }
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index 73a7d433b5c..a1d21361957 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -13,16 +13,16 @@ use rustc_session::config::ProcMacroExecutionStrategy;
 use rustc_span::profiling::SpannedEventArgRecorder;
 use rustc_span::{Span, DUMMY_SP};
 
-struct CrossbeamMessagePipe<T> {
-    tx: crossbeam_channel::Sender<T>,
-    rx: crossbeam_channel::Receiver<T>,
+struct MessagePipe<T> {
+    tx: std::sync::mpsc::SyncSender<T>,
+    rx: std::sync::mpsc::Receiver<T>,
 }
 
-impl<T> pm::bridge::server::MessagePipe<T> for CrossbeamMessagePipe<T> {
+impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
     fn new() -> (Self, Self) {
-        let (tx1, rx1) = crossbeam_channel::bounded(1);
-        let (tx2, rx2) = crossbeam_channel::bounded(1);
-        (CrossbeamMessagePipe { tx: tx1, rx: rx2 }, CrossbeamMessagePipe { tx: tx2, rx: rx1 })
+        let (tx1, rx1) = std::sync::mpsc::sync_channel(1);
+        let (tx2, rx2) = std::sync::mpsc::sync_channel(1);
+        (MessagePipe { tx: tx1, rx: rx2 }, MessagePipe { tx: tx2, rx: rx1 })
     }
 
     fn send(&mut self, value: T) {
@@ -35,7 +35,7 @@ impl<T> pm::bridge::server::MessagePipe<T> for CrossbeamMessagePipe<T> {
 }
 
 fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy {
-    pm::bridge::server::MaybeCrossThread::<CrossbeamMessagePipe<_>>::new(
+    pm::bridge::server::MaybeCrossThread::<MessagePipe<_>>::new(
         ecx.sess.opts.unstable_opts.proc_macro_execution_strategy
             == ProcMacroExecutionStrategy::CrossThread,
     )
@@ -171,7 +171,7 @@ impl MultiItemModifier for DeriveProcMacro {
                         items.push(Annotatable::Item(item));
                     }
                 }
-                Err(mut err) => {
+                Err(err) => {
                     err.emit();
                     break;
                 }
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 5eb6aed7253..6392894fea2 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -379,8 +379,8 @@ impl ToInternal<SmallVec<[tokenstream::TokenTree; 2]>>
 impl ToInternal<rustc_errors::Level> for Level {
     fn to_internal(self) -> rustc_errors::Level {
         match self {
-            Level::Error => rustc_errors::Level::Error { lint: false },
-            Level::Warning => rustc_errors::Level::Warning(None),
+            Level::Error => rustc_errors::Level::Error,
+            Level::Warning => rustc_errors::Level::Warning,
             Level::Note => rustc_errors::Level::Note,
             Level::Help => rustc_errors::Level::Help,
             _ => unreachable!("unknown proc_macro::Level variant: {:?}", self),
@@ -497,7 +497,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
     fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
         let mut diag =
             rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message);
-        diag.set_span(MultiSpan::from_spans(diagnostic.spans));
+        diag.span(MultiSpan::from_spans(diagnostic.spans));
         for child in diagnostic.children {
             diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans));
         }
@@ -537,7 +537,7 @@ impl server::TokenStream for Rustc<'_, '_> {
             }
             expr
         };
-        let expr = expr.map_err(|mut err| {
+        let expr = expr.map_err(|err| {
             err.emit();
         })?;
 
diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs
index 0b859841828..3c14ad5e7b8 100644
--- a/compiler/rustc_expand/src/tests.rs
+++ b/compiler/rustc_expand/src/tests.rs
@@ -7,7 +7,7 @@ use rustc_span::source_map::{FilePathMapping, SourceMap};
 use rustc_span::{BytePos, Span};
 
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::emitter::EmitterWriter;
+use rustc_errors::emitter::HumanEmitter;
 use rustc_errors::{DiagCtxt, MultiSpan, PResult};
 use termcolor::WriteColor;
 
@@ -30,7 +30,7 @@ fn create_test_handler() -> (DiagCtxt, Lrc<SourceMap>, Arc<Mutex<Vec<u8>>>) {
         vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE],
         false,
     );
-    let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
+    let emitter = HumanEmitter::new(Box::new(Shared { data: output.clone() }), fallback_bundle)
         .sm(Some(source_map.clone()))
         .diagnostic_width(Some(140));
     let dcx = DiagCtxt::with_emitter(Box::new(emitter));
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index e6faad74384..59ea828440f 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -210,7 +210,7 @@ declare_features! (
     /// Allows the `multiple_supertrait_upcastable` lint.
     (unstable, multiple_supertrait_upcastable, "1.69.0", None),
     /// Allow negative trait bounds. This is an internal-only feature for testing the trait solver!
-    (incomplete, negative_bounds, "1.71.0", None),
+    (internal, negative_bounds, "1.71.0", None),
     /// Allows using `#[omit_gdb_pretty_printer_section]`.
     (internal, omit_gdb_pretty_printer_section, "1.5.0", None),
     /// Allows using `#[prelude_import]` on glob `use` items.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2c34fc13919..58ac9668da5 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1258,7 +1258,7 @@ pub struct Arm<'hir> {
     /// If this pattern and the optional guard matches, then `body` is evaluated.
     pub pat: &'hir Pat<'hir>,
     /// Optional guard clause.
-    pub guard: Option<Guard<'hir>>,
+    pub guard: Option<&'hir Expr<'hir>>,
     /// The expression the arm evaluates to if this arm matches.
     pub body: &'hir Expr<'hir>,
 }
@@ -1281,26 +1281,6 @@ pub struct Let<'hir> {
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
-pub enum Guard<'hir> {
-    If(&'hir Expr<'hir>),
-    IfLet(&'hir Let<'hir>),
-}
-
-impl<'hir> Guard<'hir> {
-    /// Returns the body of the guard
-    ///
-    /// In other words, returns the e in either of the following:
-    ///
-    /// - `if e`
-    /// - `if let x = e`
-    pub fn body(&self) -> &'hir Expr<'hir> {
-        match self {
-            Guard::If(e) | Guard::IfLet(Let { init: e, .. }) => e,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct ExprField<'hir> {
     #[stable_hasher(ignore)]
     pub hir_id: HirId,
@@ -1381,6 +1361,12 @@ impl CoroutineKind {
     }
 }
 
+impl CoroutineKind {
+    pub fn is_fn_like(self) -> bool {
+        matches!(self, CoroutineKind::Desugared(_, CoroutineSource::Fn))
+    }
+}
+
 impl fmt::Display for CoroutineKind {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
@@ -2017,7 +2003,7 @@ pub enum LocalSource {
     AsyncFn,
     /// A desugared `<expr>.await`.
     AwaitDesugar,
-    /// A desugared `expr = expr`, where the LHS is a tuple, struct or array.
+    /// A desugared `expr = expr`, where the LHS is a tuple, struct, array or underscore expression.
     /// The span is that of the `=` sign.
     AssignDesugar(Span),
 }
@@ -2419,6 +2405,39 @@ impl<'hir> Ty<'hir> {
         my_visitor.visit_ty(self);
         my_visitor.0
     }
+
+    /// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to
+    /// use inference to provide suggestions for the appropriate type if possible.
+    pub fn is_suggestable_infer_ty(&self) -> bool {
+        fn are_suggestable_generic_args(generic_args: &[GenericArg<'_>]) -> bool {
+            generic_args.iter().any(|arg| match arg {
+                GenericArg::Type(ty) => ty.is_suggestable_infer_ty(),
+                GenericArg::Infer(_) => true,
+                _ => false,
+            })
+        }
+        debug!(?self);
+        match &self.kind {
+            TyKind::Infer => true,
+            TyKind::Slice(ty) => ty.is_suggestable_infer_ty(),
+            TyKind::Array(ty, length) => {
+                ty.is_suggestable_infer_ty() || matches!(length, ArrayLen::Infer(_, _))
+            }
+            TyKind::Tup(tys) => tys.iter().any(Self::is_suggestable_infer_ty),
+            TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => mut_ty.ty.is_suggestable_infer_ty(),
+            TyKind::OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args),
+            TyKind::Path(QPath::TypeRelative(ty, segment)) => {
+                ty.is_suggestable_infer_ty() || are_suggestable_generic_args(segment.args().args)
+            }
+            TyKind::Path(QPath::Resolved(ty_opt, Path { segments, .. })) => {
+                ty_opt.is_some_and(Self::is_suggestable_infer_ty)
+                    || segments
+                        .iter()
+                        .any(|segment| are_suggestable_generic_args(segment.args().args))
+            }
+            _ => false,
+        }
+    }
 }
 
 /// Not represented directly in the AST; referred to by name through a `ty_path`.
@@ -2749,7 +2768,7 @@ pub enum FnRetTy<'hir> {
     Return(&'hir Ty<'hir>),
 }
 
-impl FnRetTy<'_> {
+impl<'hir> FnRetTy<'hir> {
     #[inline]
     pub fn span(&self) -> Span {
         match *self {
@@ -2757,6 +2776,15 @@ impl FnRetTy<'_> {
             Self::Return(ref ty) => ty.span,
         }
     }
+
+    pub fn get_infer_ret_ty(&self) -> Option<&'hir Ty<'hir>> {
+        if let Self::Return(ty) = self {
+            if ty.is_suggestable_infer_ty() {
+                return Some(*ty);
+            }
+        }
+        None
+    }
 }
 
 /// Represents `for<...>` binder before a closure
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index e58e4c8fe0e..dd3633b6b4f 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -619,13 +619,8 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
 pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
     visitor.visit_id(arm.hir_id);
     visitor.visit_pat(arm.pat);
-    if let Some(ref g) = arm.guard {
-        match g {
-            Guard::If(ref e) => visitor.visit_expr(e),
-            Guard::IfLet(ref l) => {
-                visitor.visit_let_expr(l);
-            }
-        }
+    if let Some(ref e) = arm.guard {
+        visitor.visit_expr(e);
     }
     visitor.visit_expr(arm.body);
 }
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
index b671bebeb05..b5ebc1fab76 100644
--- a/compiler/rustc_hir_analysis/Cargo.toml
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -17,6 +17,7 @@ rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
 rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
 rustc_lint_defs = { path = "../rustc_lint_defs" }
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index d8b6b9a1272..6a17668ad17 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -346,6 +346,20 @@ hir_analysis_start_not_target_feature = `#[start]` function is not allowed to ha
 hir_analysis_start_not_track_caller = `#[start]` function is not allowed to be `#[track_caller]`
     .label = `#[start]` function is not allowed to be `#[track_caller]`
 
+hir_analysis_static_mut_ref = reference of mutable static is disallowed
+    .label = reference of mutable static
+    .note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+    .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+    .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+
+hir_analysis_static_mut_ref_lint = {$shared}reference of mutable static is discouraged
+    .label = shared reference of mutable static
+    .label_mut = mutable reference of mutable static
+    .suggestion = shared references are dangerous since if there's any kind of mutation of that static while the reference lives, that's UB; use `addr_of!` instead to create a raw pointer
+    .suggestion_mut = mutable references are dangerous since if there's any other pointer or reference used for that static while the reference lives, that's UB; use `addr_of_mut!` instead to create a raw pointer
+    .note = reference of mutable static is a hard error from 2024 edition
+    .why_note = mutable statics can be written to by multiple threads: aliasing violations or data races will cause undefined behavior
+
 hir_analysis_static_specialize = cannot specialize on `'static` lifetime
 
 hir_analysis_substs_on_overridden_impl = could not resolve substs on overridden impl
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index 91b3807d744..1f88aaa6a4b 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -26,23 +26,36 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
         span: Span,
     ) {
         let tcx = self.tcx();
+        let sized_def_id = tcx.lang_items().sized_trait();
+        let mut seen_negative_sized_bound = false;
 
         // Try to find an unbound in bounds.
         let mut unbounds: SmallVec<[_; 1]> = SmallVec::new();
         let mut search_bounds = |ast_bounds: &'tcx [hir::GenericBound<'tcx>]| {
             for ab in ast_bounds {
-                if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
-                    unbounds.push(ptr)
+                let hir::GenericBound::Trait(ptr, modifier) = ab else {
+                    continue;
+                };
+                match modifier {
+                    hir::TraitBoundModifier::Maybe => unbounds.push(ptr),
+                    hir::TraitBoundModifier::Negative => {
+                        if let Some(sized_def_id) = sized_def_id
+                            && ptr.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
+                        {
+                            seen_negative_sized_bound = true;
+                        }
+                    }
+                    _ => {}
                 }
             }
         };
         search_bounds(ast_bounds);
         if let Some((self_ty, where_clause)) = self_ty_where_predicates {
             for clause in where_clause {
-                if let hir::WherePredicate::BoundPredicate(pred) = clause {
-                    if pred.is_param_bound(self_ty.to_def_id()) {
-                        search_bounds(pred.bounds);
-                    }
+                if let hir::WherePredicate::BoundPredicate(pred) = clause
+                    && pred.is_param_bound(self_ty.to_def_id())
+                {
+                    search_bounds(pred.bounds);
                 }
             }
         }
@@ -53,15 +66,13 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
             });
         }
 
-        let sized_def_id = tcx.lang_items().sized_trait();
-
         let mut seen_sized_unbound = false;
         for unbound in unbounds {
-            if let Some(sized_def_id) = sized_def_id {
-                if unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id) {
-                    seen_sized_unbound = true;
-                    continue;
-                }
+            if let Some(sized_def_id) = sized_def_id
+                && unbound.trait_ref.path.res == Res::Def(DefKind::Trait, sized_def_id)
+            {
+                seen_sized_unbound = true;
+                continue;
             }
             // There was a `?Trait` bound, but it was not `?Sized`; warn.
             tcx.dcx().span_warn(
@@ -71,15 +82,12 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
             );
         }
 
-        // If the above loop finished there was no `?Sized` bound; add implicitly sized if `Sized` is available.
-        if sized_def_id.is_none() {
-            // No lang item for `Sized`, so we can't add it as a bound.
-            return;
-        }
-        if seen_sized_unbound {
-            // There was in fact a `?Sized` bound, return without doing anything
-        } else {
-            // There was no `?Sized` bound; add implicitly sized if `Sized` is available.
+        if seen_sized_unbound || seen_negative_sized_bound {
+            // There was in fact a `?Sized` or `!Sized` bound;
+            // we don't need to do anything.
+        } else if sized_def_id.is_some() {
+            // There was no `?Sized` or `!Sized` bound;
+            // add `Sized` if it's available.
             bounds.push_sized(tcx, self_ty, span);
         }
     }
@@ -297,7 +305,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                     binding.span,
                     format!("{} `{}` is private", assoc_item.kind, binding.item_name),
                 )
-                .span_label(binding.span, format!("private {}", assoc_item.kind))
+                .with_span_label(binding.span, format!("private {}", assoc_item.kind))
                 .emit();
         }
         tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
@@ -454,7 +462,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                     late_bound_in_trait_ref,
                     late_bound_in_ty,
                     |br_name| {
-                        struct_span_err!(
+                        struct_span_code_err!(
                             tcx.dcx(),
                             binding.span,
                             E0582,
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index f17f19bb77c..fc2ed104b3d 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -6,7 +6,8 @@ use crate::errors::{
 use crate::fluent_generated as fluent;
 use crate::traits::error_reporting::report_object_safety_error;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
+use rustc_data_structures::unord::UnordMap;
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::traits::FulfillmentError;
@@ -57,13 +58,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         if !trait_def.paren_sugar {
             if trait_segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar {
                 // For now, require that parenthetical notation be used only with `Fn()` etc.
-                let mut err = feature_err(
+                feature_err(
                     &self.tcx().sess.parse_sess,
                     sym::unboxed_closures,
                     span,
                     "parenthetical notation is only stable when used with `Fn`-family traits",
-                );
-                err.emit();
+                )
+                .emit();
             }
 
             return;
@@ -345,7 +346,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         candidates: Vec<DefId>,
         span: Span,
     ) -> ErrorGuaranteed {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.tcx().dcx(),
             name.span,
             E0034,
@@ -444,7 +445,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 String::new()
             };
 
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 name.span,
                 E0220,
@@ -605,7 +606,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let violations =
                     object_safety_violations_for_assoc_item(tcx, trait_def_id, *assoc_item);
                 if !violations.is_empty() {
-                    report_object_safety_error(tcx, *span, trait_def_id, &violations).emit();
+                    report_object_safety_error(tcx, *span, None, trait_def_id, &violations).emit();
                     object_safety_violations = true;
                 }
             }
@@ -673,7 +674,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 }))
             })
             .flatten()
-            .collect::<FxHashMap<Symbol, &ty::AssocItem>>();
+            .collect::<UnordMap<Symbol, &ty::AssocItem>>();
 
         let mut names = names
             .into_iter()
@@ -696,7 +697,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let names = names.join(", ");
 
         trait_bound_spans.sort();
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             tcx.dcx(),
             trait_bound_spans,
             E0191,
@@ -709,7 +710,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let mut where_constraints = vec![];
         let mut already_has_generics_args_suggestion = false;
         for (span, assoc_items) in &associated_types {
-            let mut names: FxHashMap<_, usize> = FxHashMap::default();
+            let mut names: UnordMap<_, usize> = Default::default();
             for item in assoc_items {
                 types_count += 1;
                 *names.entry(item.name).or_insert(0) += 1;
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index 7d840ba7e81..e2cd4d5f21c 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -5,7 +5,7 @@ use crate::astconv::{
 };
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -27,7 +27,7 @@ fn generic_arg_mismatch_err(
     help: Option<String>,
 ) -> ErrorGuaranteed {
     let sess = tcx.sess;
-    let mut err = struct_span_err!(
+    let mut err = struct_span_code_err!(
         tcx.dcx(),
         arg.span(),
         E0747,
@@ -70,7 +70,7 @@ fn generic_arg_mismatch_err(
             Res::Err => {
                 add_braces_suggestion(arg, &mut err);
                 return err
-                    .set_primary_message("unresolved item provided when a constant was expected")
+                    .with_primary_message("unresolved item provided when a constant was expected")
                     .emit();
             }
             Res::Def(DefKind::TyParam, src_def_id) => {
@@ -650,9 +650,9 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
         if position == GenericArgPosition::Value
             && args.num_lifetime_params() != param_counts.lifetimes
         {
-            let mut err = struct_span_err!(tcx.dcx(), span, E0794, "{}", msg);
-            err.span_note(span_late, note);
-            err.emit();
+            struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg)
+                .with_span_note(span_late, note)
+                .emit();
         } else {
             let mut multispan = MultiSpan::from_span(span);
             multispan.push_span_label(span_late, note);
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
index f3b93c91ae9..3761d529517 100644
--- a/compiler/rustc_hir_analysis/src/astconv/lint.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -1,7 +1,9 @@
 use rustc_ast::TraitObjectSyntax;
 use rustc_errors::{Diagnostic, StashKey};
 use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
 use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
+use rustc_span::Span;
 use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
 
 use super::AstConv;
@@ -32,32 +34,146 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
             let of_trait_span = of_trait_ref.path.span;
             // make sure that we are not calling unwrap to abort during the compilation
-            let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
-                return;
-            };
             let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else {
                 return;
             };
-            // check if the trait has generics, to make a correct suggestion
-            let param_name = generics.params.next_type_param_name(None);
 
-            let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
-                (span, format!(", {param_name}: {impl_trait_name}"))
-            } else {
-                (generics.span, format!("<{param_name}: {impl_trait_name}>"))
+            let Ok(impl_trait_name) = self.tcx().sess.source_map().span_to_snippet(self_ty.span)
+            else {
+                return;
+            };
+            let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &impl_trait_name);
+            if sugg.is_empty() {
+                return;
             };
             diag.multipart_suggestion(
                 format!(
-                    "alternatively use a blanket \
-                     implementation to implement `{of_trait_name}` for \
+                    "alternatively use a blanket implementation to implement `{of_trait_name}` for \
                      all types that also implement `{impl_trait_name}`"
                 ),
-                vec![(self_ty.span, param_name), add_generic_sugg],
+                sugg,
                 Applicability::MaybeIncorrect,
             );
         }
     }
 
+    fn add_generic_param_suggestion(
+        &self,
+        generics: &hir::Generics<'_>,
+        self_ty_span: Span,
+        impl_trait_name: &str,
+    ) -> Vec<(Span, String)> {
+        // check if the trait has generics, to make a correct suggestion
+        let param_name = generics.params.next_type_param_name(None);
+
+        let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
+            (span, format!(", {param_name}: {impl_trait_name}"))
+        } else {
+            (generics.span, format!("<{param_name}: {impl_trait_name}>"))
+        };
+        vec![(self_ty_span, param_name), add_generic_sugg]
+    }
+
+    /// Make sure that we are in the condition to suggest `impl Trait`.
+    fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool {
+        let tcx = self.tcx();
+        let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
+        let (hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, generics, _), .. })
+        | hir::Node::TraitItem(hir::TraitItem {
+            kind: hir::TraitItemKind::Fn(sig, _),
+            generics,
+            ..
+        })) = tcx.hir_node_by_def_id(parent_id)
+        else {
+            return false;
+        };
+        let Ok(trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else {
+            return false;
+        };
+        let impl_sugg = vec![(self_ty.span.shrink_to_lo(), "impl ".to_string())];
+        let is_object_safe = match self_ty.kind {
+            hir::TyKind::TraitObject(objects, ..) => {
+                objects.iter().all(|o| match o.trait_ref.path.res {
+                    Res::Def(DefKind::Trait, id) => tcx.check_is_object_safe(id),
+                    _ => false,
+                })
+            }
+            _ => false,
+        };
+        if let hir::FnRetTy::Return(ty) = sig.decl.output
+            && ty.hir_id == self_ty.hir_id
+        {
+            let pre = if !is_object_safe {
+                format!("`{trait_name}` is not object safe, ")
+            } else {
+                String::new()
+            };
+            let msg = format!(
+                "{pre}use `impl {trait_name}` to return an opaque type, as long as you return a \
+                 single underlying type",
+            );
+            diag.multipart_suggestion_verbose(msg, impl_sugg, Applicability::MachineApplicable);
+            if is_object_safe {
+                diag.multipart_suggestion_verbose(
+                    "alternatively, you can return an owned trait object",
+                    vec![
+                        (ty.span.shrink_to_lo(), "Box<dyn ".to_string()),
+                        (ty.span.shrink_to_hi(), ">".to_string()),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            } else {
+                // We'll emit the object safety error already, with a structured suggestion.
+                diag.downgrade_to_delayed_bug();
+            }
+            return true;
+        }
+        for ty in sig.decl.inputs {
+            if ty.hir_id != self_ty.hir_id {
+                continue;
+            }
+            let sugg = self.add_generic_param_suggestion(generics, self_ty.span, &trait_name);
+            if !sugg.is_empty() {
+                diag.multipart_suggestion_verbose(
+                    format!("use a new generic type parameter, constrained by `{trait_name}`"),
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+                diag.multipart_suggestion_verbose(
+                    "you can also use an opaque type, but users won't be able to specify the type \
+                     parameter when calling the `fn`, having to rely exclusively on type inference",
+                    impl_sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+            if !is_object_safe {
+                diag.note(format!("`{trait_name}` it is not object safe, so it can't be `dyn`"));
+                // We'll emit the object safety error already, with a structured suggestion.
+                diag.downgrade_to_delayed_bug();
+            } else {
+                let sugg = if let hir::TyKind::TraitObject([_, _, ..], _, _) = self_ty.kind {
+                    // There are more than one trait bound, we need surrounding parentheses.
+                    vec![
+                        (self_ty.span.shrink_to_lo(), "&(dyn ".to_string()),
+                        (self_ty.span.shrink_to_hi(), ")".to_string()),
+                    ]
+                } else {
+                    vec![(self_ty.span.shrink_to_lo(), "&dyn ".to_string())]
+                };
+                diag.multipart_suggestion_verbose(
+                    format!(
+                        "alternatively, use a trait object to accept any type that implements \
+                         `{trait_name}`, accessing its methods at runtime using dynamic dispatch",
+                    ),
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
+            return true;
+        }
+        false
+    }
+
     pub(super) fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
         let tcx = self.tcx();
         if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
@@ -97,8 +213,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let msg = "trait objects must include the `dyn` keyword";
                 let label = "add `dyn` keyword before this trait";
                 let mut diag =
-                    rustc_errors::struct_span_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
-                if self_ty.span.can_be_used_for_suggestions() {
+                    rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
+                if self_ty.span.can_be_used_for_suggestions()
+                    && !self.maybe_lint_impl_trait(self_ty, &mut diag)
+                {
                     diag.multipart_suggestion_verbose(
                         label,
                         sugg,
@@ -116,11 +234,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     self_ty.span,
                     msg,
                     |lint| {
-                        lint.multipart_suggestion_verbose(
-                            "use `dyn`",
-                            sugg,
-                            Applicability::MachineApplicable,
-                        );
+                        if self_ty.span.can_be_used_for_suggestions()
+                            && !self.maybe_lint_impl_trait(self_ty, lint)
+                        {
+                            lint.multipart_suggestion_verbose(
+                                "use `dyn`",
+                                sugg,
+                                Applicability::MachineApplicable,
+                            );
+                        }
                         self.maybe_lint_blanket_trait_impl(self_ty, lint);
                     },
                 );
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 092df257dbf..1f47564649e 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{
-    error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    FatalError, MultiSpan,
+    error_code, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
+    ErrorGuaranteed, FatalError, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@@ -866,7 +866,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         traits: &[String],
         name: Symbol,
     ) -> ErrorGuaranteed {
-        let mut err = struct_span_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type");
+        let mut err =
+            struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type");
         if self
             .tcx()
             .resolutions(())
@@ -1032,7 +1033,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             self.trait_defines_associated_item_named(r.def_id(), assoc_kind, assoc_name)
         });
 
-        let Some(mut bound) = matching_candidates.next() else {
+        let Some(bound) = matching_candidates.next() else {
             let reported = self.complain_about_assoc_item_not_found(
                 all_candidates,
                 &ty_param_name.to_string(),
@@ -1046,38 +1047,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         };
         debug!(?bound);
 
-        // look for a candidate that is not the same as our first bound, disregarding
-        // whether the bound is const.
-        let mut next_cand = matching_candidates.next();
-        while let Some(mut bound2) = next_cand {
-            debug!(?bound2);
-            if bound2.bound_vars() != bound.bound_vars() {
-                break;
-            }
-
-            let generics = tcx.generics_of(bound.def_id());
-            let Some(host_index) = generics.host_effect_index else { break };
-
-            // always return the bound that contains the host param.
-            if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() {
-                (bound, bound2) = (bound2, bound);
-            }
-
-            let unconsted_args = bound
-                .skip_binder()
-                .args
-                .iter()
-                .enumerate()
-                .map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
-
-            if unconsted_args.eq(bound2.skip_binder().args.iter()) {
-                next_cand = matching_candidates.next();
-            } else {
-                break;
-            }
-        }
-
-        if let Some(bound2) = next_cand {
+        if let Some(bound2) = matching_candidates.next() {
             debug!(?bound2);
 
             let assoc_kind_str = assoc_kind_str(assoc_kind);
@@ -1344,7 +1314,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     let msg = format!("expected type, found variant `{assoc_ident}`");
                     tcx.dcx().span_err(span, msg)
                 } else if qself_ty.is_enum() {
-                    let mut err = struct_span_err!(
+                    let mut err = struct_span_code_err!(
                         tcx.dcx(),
                         assoc_ident.span,
                         E0599,
@@ -1385,7 +1355,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     reported
                 } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
                     // `<impl Trait as OtherTrait>::Assoc` makes no sense.
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         tcx.def_span(alias_ty.def_id),
                         E0667,
@@ -1647,9 +1617,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let msg = format!("{kind} `{name}` is private");
             let def_span = tcx.def_span(item);
             tcx.dcx()
-                .struct_span_err_with_code(span, msg, rustc_errors::error_code!(E0624))
-                .span_label(span, format!("private {kind}"))
-                .span_label(def_span, format!("{kind} defined here"))
+                .struct_span_err(span, msg)
+                .with_code(rustc_errors::error_code!(E0624))
+                .with_span_label(span, format!("private {kind}"))
+                .with_span_label(def_span, format!("{kind} defined here"))
                 .emit();
         }
         tcx.check_stability(item, Some(block), span, None);
@@ -1880,7 +1851,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             };
             let last_span = *arg_spans.last().unwrap();
             let span: MultiSpan = arg_spans.into();
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 self.tcx().dcx(),
                 span,
                 E0109,
@@ -2631,7 +2602,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
 
         self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 decl.output.span(),
                 E0581,
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
index a614d4abf25..ea2f5f50b5c 100644
--- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -2,7 +2,7 @@ use crate::astconv::{GenericArgCountMismatch, GenericArgCountResult, OnlySelfBou
 use crate::bounds::Bounds;
 use crate::errors::TraitObjectDeclaredWithNoTraits;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -89,7 +89,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         if regular_traits.len() > 1 {
             let first_trait = &regular_traits[0];
             let additional_trait = &regular_traits[1];
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 additional_trait.bottom().1,
                 E0225,
@@ -140,6 +140,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 let reported = report_object_safety_error(
                     tcx,
                     span,
+                    Some(hir_id),
                     item.trait_ref().def_id(),
                     &object_safety_violations,
                 )
@@ -289,19 +290,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
 
                 if references_self {
                     let def_id = i.bottom().0.def_id();
-                    let mut err = struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         i.bottom().1,
                         E0038,
                         "the {} `{}` cannot be made into an object",
                         tcx.def_descr(def_id),
                         tcx.item_name(def_id),
-                    );
-                    err.note(
+                    )
+                    .with_note(
                         rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
                             .error_msg(),
-                    );
-                    err.emit();
+                    )
+                    .emit();
                 }
 
                 ty::ExistentialTraitRef { def_id: trait_ref.def_id, args }
@@ -374,7 +375,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     self.ast_region_to_region(lifetime, None)
                 } else {
                     self.re_infer(None, span).unwrap_or_else(|| {
-                        let mut err = struct_span_err!(
+                        let err = struct_span_code_err!(
                             tcx.dcx(),
                             span,
                             E0228,
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index b6688e0ce29..b69f679880d 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -45,24 +45,6 @@ impl<'tcx> Bounds<'tcx> {
         polarity: ty::ImplPolarity,
     ) {
         self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
-
-        // push a non-const (`host = true`) version of the bound if it is `~const`.
-        if tcx.features().effects
-            && let Some(host_effect_idx) = tcx.generics_of(trait_ref.def_id()).host_effect_index
-            && trait_ref.skip_binder().args.const_at(host_effect_idx) != tcx.consts.true_
-        {
-            let generics = tcx.generics_of(trait_ref.def_id());
-            let Some(host_index) = generics.host_effect_index else { return };
-            let trait_ref = trait_ref.map_bound(|mut trait_ref| {
-                trait_ref.args =
-                    tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| {
-                        if host_index == n { tcx.consts.true_.into() } else { arg }
-                    }));
-                trait_ref
-            });
-
-            self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
-        }
     }
 
     fn push_trait_bound_inner(
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index d2e96ac74df..6265ddafef0 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -8,7 +8,7 @@ use rustc_attr as attr;
 use rustc_errors::{ErrorGuaranteed, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind};
-use rustc_hir::def_id::LocalModDefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::Node;
 use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::{Obligation, TraitEngineExt as _};
@@ -17,7 +17,7 @@ use rustc_middle::middle::stability::EvalResult;
 use rustc_middle::traits::{DefiningAnchor, ObligationCauseCode};
 use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
-use rustc_middle::ty::util::{Discr, IntTypeExt};
+use rustc_middle::ty::util::{Discr, InspectCoroutineFields, IntTypeExt};
 use rustc_middle::ty::GenericArgKind;
 use rustc_middle::ty::{
     AdtDef, ParamEnv, RegionKind, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
@@ -37,7 +37,7 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
     match tcx.sess.target.is_abi_supported(abi) {
         Some(true) => (),
         Some(false) => {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 span,
                 E0570,
@@ -58,7 +58,7 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
 
     // This ABI is only allowed on function pointers
     if abi == Abi::CCmseNonSecureCall {
-        struct_span_err!(
+        struct_span_code_err!(
             tcx.dcx(),
             span,
             E0781,
@@ -198,8 +198,8 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
 /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
 /// projections that would result in "inheriting lifetimes".
-fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
-    let item = tcx.hir().item(id);
+fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+    let item = tcx.hir().expect_item(def_id);
     let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
         tcx.dcx().span_delayed_bug(item.span, "expected opaque item");
         return;
@@ -213,13 +213,12 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
         return;
     }
 
-    let args = GenericArgs::identity_for_item(tcx, item.owner_id);
     let span = tcx.def_span(item.owner_id.def_id);
 
     if tcx.type_of(item.owner_id.def_id).instantiate_identity().references_error() {
         return;
     }
-    if check_opaque_for_cycles(tcx, item.owner_id.def_id, args, span, origin).is_err() {
+    if check_opaque_for_cycles(tcx, item.owner_id.def_id, span).is_err() {
         return;
     }
 
@@ -230,19 +229,36 @@ fn check_opaque(tcx: TyCtxt<'_>, id: hir::ItemId) {
 pub(super) fn check_opaque_for_cycles<'tcx>(
     tcx: TyCtxt<'tcx>,
     def_id: LocalDefId,
-    args: GenericArgsRef<'tcx>,
     span: Span,
-    origin: &hir::OpaqueTyOrigin,
 ) -> Result<(), ErrorGuaranteed> {
-    if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() {
-        let reported = match origin {
-            hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
-            _ => opaque_type_cycle_error(tcx, def_id, span),
-        };
-        Err(reported)
-    } else {
-        Ok(())
+    let args = GenericArgs::identity_for_item(tcx, def_id);
+
+    // First, try to look at any opaque expansion cycles, considering coroutine fields
+    // (even though these aren't necessarily true errors).
+    if tcx
+        .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::Yes)
+        .is_err()
+    {
+        // Look for true opaque expansion cycles, but ignore coroutines.
+        // This will give us any true errors. Coroutines are only problematic
+        // if they cause layout computation errors.
+        if tcx
+            .try_expand_impl_trait_type(def_id.to_def_id(), args, InspectCoroutineFields::No)
+            .is_err()
+        {
+            let reported = opaque_type_cycle_error(tcx, def_id, span);
+            return Err(reported);
+        }
+
+        // And also look for cycle errors in the layout of coroutines.
+        if let Err(&LayoutError::Cycle(guar)) =
+            tcx.layout_of(tcx.param_env(def_id).and(Ty::new_opaque(tcx, def_id.to_def_id(), args)))
+        {
+            return Err(guar);
+        }
     }
+
+    Ok(())
 }
 
 /// Check that the concrete type behind `impl Trait` actually implements `Trait`.
@@ -440,40 +456,31 @@ fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     }
 }
 
-fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
-    debug!(
-        "check_item_type(it.def_id={:?}, it.name={})",
-        id.owner_id,
-        tcx.def_path_str(id.owner_id)
-    );
+pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let _indenter = indenter();
-    match tcx.def_kind(id.owner_id) {
+    match tcx.def_kind(def_id) {
         DefKind::Static(..) => {
-            tcx.ensure().typeck(id.owner_id.def_id);
-            maybe_check_static_with_link_section(tcx, id.owner_id.def_id);
-            check_static_inhabited(tcx, id.owner_id.def_id);
-            check_static_linkage(tcx, id.owner_id.def_id);
+            tcx.ensure().typeck(def_id);
+            maybe_check_static_with_link_section(tcx, def_id);
+            check_static_inhabited(tcx, def_id);
+            check_static_linkage(tcx, def_id);
         }
         DefKind::Const => {
-            tcx.ensure().typeck(id.owner_id.def_id);
+            tcx.ensure().typeck(def_id);
         }
         DefKind::Enum => {
-            check_enum(tcx, id.owner_id.def_id);
+            check_enum(tcx, def_id);
         }
         DefKind::Fn => {} // entirely within check_item_body
         DefKind::Impl { of_trait } => {
-            if of_trait && let Some(impl_trait_ref) = tcx.impl_trait_ref(id.owner_id) {
-                check_impl_items_against_trait(
-                    tcx,
-                    id.owner_id.def_id,
-                    impl_trait_ref.instantiate_identity(),
-                );
-                check_on_unimplemented(tcx, id);
+            if of_trait && let Some(impl_trait_ref) = tcx.impl_trait_ref(def_id) {
+                check_impl_items_against_trait(tcx, def_id, impl_trait_ref.instantiate_identity());
+                check_on_unimplemented(tcx, def_id);
             }
         }
         DefKind::Trait => {
-            let assoc_items = tcx.associated_items(id.owner_id);
-            check_on_unimplemented(tcx, id);
+            let assoc_items = tcx.associated_items(def_id);
+            check_on_unimplemented(tcx, def_id);
 
             for &assoc_item in assoc_items.in_definition_order() {
                 match assoc_item.kind {
@@ -482,12 +489,12 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
                         forbid_intrinsic_abi(tcx, assoc_item.ident(tcx).span, abi);
                     }
                     ty::AssocKind::Type if assoc_item.defaultness(tcx).has_value() => {
-                        let trait_args = GenericArgs::identity_for_item(tcx, id.owner_id);
+                        let trait_args = GenericArgs::identity_for_item(tcx, def_id);
                         let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds(
                             tcx,
                             assoc_item,
                             assoc_item,
-                            ty::TraitRef::new(tcx, id.owner_id.to_def_id(), trait_args),
+                            ty::TraitRef::new(tcx, def_id.to_def_id(), trait_args),
                         );
                     }
                     _ => {}
@@ -495,13 +502,13 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
             }
         }
         DefKind::Struct => {
-            check_struct(tcx, id.owner_id.def_id);
+            check_struct(tcx, def_id);
         }
         DefKind::Union => {
-            check_union(tcx, id.owner_id.def_id);
+            check_union(tcx, def_id);
         }
         DefKind::OpaqueTy => {
-            let origin = tcx.opaque_type_origin(id.owner_id.def_id);
+            let origin = tcx.opaque_type_origin(def_id);
             if let hir::OpaqueTyOrigin::FnReturn(fn_def_id)
             | hir::OpaqueTyOrigin::AsyncFn(fn_def_id) = origin
                 && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id)
@@ -509,16 +516,16 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
             {
                 // Skip opaques from RPIT in traits with no default body.
             } else {
-                check_opaque(tcx, id);
+                check_opaque(tcx, def_id);
             }
         }
         DefKind::TyAlias => {
-            let pty_ty = tcx.type_of(id.owner_id).instantiate_identity();
-            let generics = tcx.generics_of(id.owner_id);
+            let pty_ty = tcx.type_of(def_id).instantiate_identity();
+            let generics = tcx.generics_of(def_id);
             check_type_params_are_used(tcx, generics, pty_ty);
         }
         DefKind::ForeignMod => {
-            let it = tcx.hir().item(id);
+            let it = tcx.hir().expect_item(def_id);
             let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
                 return;
             };
@@ -553,14 +560,14 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
                                 (0, _) => ("const", "consts", None),
                                 _ => ("type or const", "types or consts", None),
                             };
-                            struct_span_err!(
+                            struct_span_code_err!(
                                 tcx.dcx(),
                                 item.span,
                                 E0044,
                                 "foreign items may not have {kinds} parameters",
                             )
-                            .span_label(item.span, format!("can't have {kinds} parameters"))
-                            .help(
+                            .with_span_label(item.span, format!("can't have {kinds} parameters"))
+                            .with_help(
                                 // FIXME: once we start storing spans for type arguments, turn this
                                 // into a suggestion.
                                 format!(
@@ -589,19 +596,19 @@ fn check_item_type(tcx: TyCtxt<'_>, id: hir::ItemId) {
             }
         }
         DefKind::GlobalAsm => {
-            let it = tcx.hir().item(id);
+            let it = tcx.hir().expect_item(def_id);
             let hir::ItemKind::GlobalAsm(asm) = it.kind else {
                 span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it)
             };
-            InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.owner_id.def_id);
+            InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, def_id);
         }
         _ => {}
     }
 }
 
-pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: hir::ItemId) {
+pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     // an error would be reported if this fails.
-    let _ = OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
+    let _ = OnUnimplementedDirective::of_item(tcx, def_id.to_def_id());
 }
 
 pub(super) fn check_specialization_validity<'tcx>(
@@ -652,10 +659,7 @@ pub(super) fn check_specialization_validity<'tcx>(
         if !tcx.is_impl_trait_in_trait(impl_item) {
             report_forbidden_specialization(tcx, impl_item, parent_impl);
         } else {
-            tcx.dcx().span_delayed_bug(
-                DUMMY_SP,
-                format!("parent item: {parent_impl:?} not marked as default"),
-            );
+            tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default"));
         }
     }
 }
@@ -680,7 +684,7 @@ fn check_impl_items_against_trait<'tcx>(
         ty::ImplPolarity::Negative => {
             if let [first_item_ref, ..] = impl_item_refs {
                 let first_item_span = tcx.def_span(first_item_ref);
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     first_item_span,
                     E0749,
@@ -797,10 +801,9 @@ fn check_impl_items_against_trait<'tcx>(
                 };
                 tcx.dcx()
                     .struct_span_err(tcx.def_span(def_id), msg)
-                    .note(format!(
-                        "specialization behaves in inconsistent and \
-                        surprising ways with {feature}, \
-                        and for now is disallowed"
+                    .with_note(format!(
+                        "specialization behaves in inconsistent and surprising ways with \
+                        {feature}, and for now is disallowed"
                     ))
                     .emit();
             }
@@ -833,13 +836,13 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
     {
         let fields = &def.non_enum_variant().fields;
         if fields.is_empty() {
-            struct_span_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
+            struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
             return;
         }
         let e = fields[FieldIdx::from_u32(0)].ty(tcx, args);
         if !fields.iter().all(|f| f.ty(tcx, args) == e) {
-            struct_span_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous")
-                .span_label(sp, "SIMD elements must have the same type")
+            struct_span_code_err!(tcx.dcx(), sp, E0076, "SIMD vector should be homogeneous")
+                .with_span_label(sp, "SIMD elements must have the same type")
                 .emit();
             return;
         }
@@ -851,10 +854,10 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
         };
         if let Some(len) = len {
             if len == 0 {
-                struct_span_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
+                struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit();
                 return;
             } else if len > MAX_SIMD_LANES {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     sp,
                     E0075,
@@ -877,7 +880,7 @@ pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
                 if matches!(t.kind(), ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)) =>
             { /* struct([f32; 4]) is ok */ }
             _ => {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     sp,
                     E0077,
@@ -900,7 +903,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
                     && let Some(repr_pack) = repr.pack
                     && pack as u64 != repr_pack.bytes()
                 {
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         sp,
                         E0634,
@@ -911,7 +914,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
             }
         }
         if repr.align.is_some() {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 sp,
                 E0587,
@@ -920,7 +923,7 @@ pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
             .emit();
         } else {
             if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) {
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     tcx.dcx(),
                     sp,
                     E0588,
@@ -1110,13 +1113,13 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 
     if def.variants().is_empty() {
         if let Some(attr) = tcx.get_attrs(def_id, sym::repr).next() {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 attr.span,
                 E0084,
                 "unsupported representation for zero-variant enum"
             )
-            .span_label(tcx.def_span(def_id), "zero-variant enum")
+            .with_span_label(tcx.def_span(def_id), "zero-variant enum")
             .emit();
         }
     }
@@ -1149,13 +1152,13 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         let disr_non_unit = def.variants().iter().any(|var| !is_unit(var) && has_disr(var));
 
         if disr_non_unit || (disr_units && has_non_units) {
-            let mut err = struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 tcx.def_span(def_id),
                 E0732,
                 "`#[repr(inttype)]` must be specified"
-            );
-            err.emit();
+            )
+            .emit();
         }
     }
 
@@ -1235,7 +1238,7 @@ fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
 
             if discrs[i].1.val == discrs[o].1.val {
                 let err = error.get_or_insert_with(|| {
-                    let mut ret = struct_span_err!(
+                    let mut ret = struct_span_code_err!(
                         tcx.dcx(),
                         tcx.def_span(adt.did()),
                         E0081,
@@ -1258,7 +1261,7 @@ fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>)
             }
         }
 
-        if let Some(mut e) = error {
+        if let Some(e) = error {
             e.emit();
         }
 
@@ -1302,33 +1305,19 @@ pub(super) fn check_type_params_are_used<'tcx>(
             && let ty::GenericParamDefKind::Type { .. } = param.kind
         {
             let span = tcx.def_span(param.def_id);
-            struct_span_err!(tcx.dcx(), span, E0091, "type parameter `{}` is unused", param.name,)
-                .span_label(span, "unused type parameter")
-                .emit();
+            struct_span_code_err!(
+                tcx.dcx(),
+                span,
+                E0091,
+                "type parameter `{}` is unused",
+                param.name,
+            )
+            .with_span_label(span, "unused type parameter")
+            .emit();
         }
     }
 }
 
-pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
-    let module = tcx.hir_module_items(module_def_id);
-    for id in module.items() {
-        check_item_type(tcx, id);
-    }
-    if module_def_id == LocalModDefId::CRATE_DEF_ID {
-        super::entry::check_for_entry_fn(tcx);
-    }
-}
-
-fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
-    struct_span_err!(tcx.dcx(), span, E0733, "recursion in an `async fn` requires boxing")
-        .span_label(span, "recursive `async fn`")
-        .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
-        .note(
-            "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
-        )
-        .emit()
-}
-
 /// Emit an error for recursive opaque types.
 ///
 /// If this is a return `impl Trait`, find the item's return expressions and point at them. For
@@ -1342,7 +1331,7 @@ fn opaque_type_cycle_error(
     opaque_def_id: LocalDefId,
     span: Span,
 ) -> ErrorGuaranteed {
-    let mut err = struct_span_err!(tcx.dcx(), span, E0720, "cannot resolve opaque type");
+    let mut err = struct_span_code_err!(tcx.dcx(), span, E0720, "cannot resolve opaque type");
 
     let mut label = false;
     if let Some((def_id, visitor)) = get_owner_return_paths(tcx, opaque_def_id) {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index dbcaa244f29..469e7a6a13c 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -1,8 +1,8 @@
 use super::potentially_plural_count;
 use crate::errors::LifetimesOrBoundsMismatchOnTrait;
-use hir::def_id::{DefId, LocalDefId};
+use hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit;
@@ -19,7 +19,7 @@ use rustc_middle::ty::{
     self, GenericArgs, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
 };
 use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
@@ -478,7 +478,7 @@ fn compare_asyncness<'tcx>(
 pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_m_def_id: LocalDefId,
-) -> Result<&'tcx FxHashMap<DefId, ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed> {
+) -> Result<&'tcx DefIdMap<ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed> {
     let impl_m = tcx.opt_associated_item(impl_m_def_id.to_def_id()).unwrap();
     let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
     let impl_trait_ref =
@@ -625,7 +625,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     match ocx.eq(&cause, param_env, trait_return_ty, impl_return_ty) {
         Ok(()) => {}
         Err(terr) => {
-            let mut diag = struct_span_err!(
+            let mut diag = struct_span_code_err!(
                 tcx.dcx(),
                 cause.span(),
                 E0053,
@@ -706,7 +706,7 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
     );
     ocx.resolve_regions_and_report_errors(impl_m_def_id, &outlives_env)?;
 
-    let mut remapped_types = FxHashMap::default();
+    let mut remapped_types = DefIdMap::default();
     for (def_id, (ty, args)) in collected_types {
         match infcx.fully_resolve((ty, args)) {
             Ok((ty, args)) => {
@@ -934,17 +934,15 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
                             return_span,
                             "return type captures more lifetimes than trait definition",
                         )
-                        .span_label(self.tcx.def_span(def_id), "this lifetime was captured")
-                        .span_note(
+                        .with_span_label(self.tcx.def_span(def_id), "this lifetime was captured")
+                        .with_span_note(
                             self.tcx.def_span(self.def_id),
                             "hidden type must only reference lifetimes captured by this impl trait",
                         )
-                        .note(format!("hidden type inferred to be `{}`", self.ty))
+                        .with_note(format!("hidden type inferred to be `{}`", self.ty))
                         .emit()
                 }
-                _ => {
-                    self.tcx.dcx().span_delayed_bug(DUMMY_SP, "should've been able to remap region")
-                }
+                _ => self.tcx.dcx().delayed_bug("should've been able to remap region"),
             };
             return Err(guar);
         };
@@ -972,7 +970,7 @@ fn report_trait_method_mismatch<'tcx>(
     let (impl_err_span, trait_err_span) =
         extract_spans_for_error_reporting(infcx, terr, &cause, impl_m, trait_m);
 
-    let mut diag = struct_span_err!(
+    let mut diag = struct_span_code_err!(
         tcx.dcx(),
         impl_err_span,
         E0053,
@@ -1217,7 +1215,7 @@ fn compare_self_type<'tcx>(
         (false, true) => {
             let self_descr = self_string(impl_m);
             let impl_m_span = tcx.def_span(impl_m.def_id);
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 impl_m_span,
                 E0185,
@@ -1237,7 +1235,7 @@ fn compare_self_type<'tcx>(
         (true, false) => {
             let self_descr = self_string(trait_m);
             let impl_m_span = tcx.def_span(impl_m.def_id);
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 impl_m_span,
                 E0186,
@@ -1303,8 +1301,7 @@ fn compare_number_of_generics<'tcx>(
     // inheriting the generics from will also have mismatched arguments, and
     // we'll report an error for that instead. Delay a bug for safety, though.
     if trait_.is_impl_trait_in_trait() {
-        return Err(tcx.dcx().span_delayed_bug(
-            rustc_span::DUMMY_SP,
+        return Err(tcx.dcx().delayed_bug(
             "errors comparing numbers of generics of trait/impl functions were not emitted",
         ));
     }
@@ -1371,7 +1368,7 @@ fn compare_number_of_generics<'tcx>(
             let spans = arg_spans(impl_.kind, impl_item.generics);
             let span = spans.first().copied();
 
-            let mut err = tcx.dcx().struct_span_err_with_code(
+            let mut err = tcx.dcx().struct_span_err(
                 spans,
                 format!(
                     "{} `{}` has {} {kind} parameter{} but its trait \
@@ -1384,8 +1381,8 @@ fn compare_number_of_generics<'tcx>(
                     pluralize!(trait_count),
                     kind = kind,
                 ),
-                DiagnosticId::Error("E0049".into()),
             );
+            err.code(DiagnosticId::Error("E0049".into()));
 
             let msg =
                 format!("expected {trait_count} {kind} parameter{}", pluralize!(trait_count),);
@@ -1463,7 +1460,7 @@ fn compare_number_of_method_arguments<'tcx>(
             })
             .unwrap_or_else(|| tcx.def_span(impl_m.def_id));
 
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             tcx.dcx(),
             impl_span,
             E0050,
@@ -1530,7 +1527,7 @@ fn compare_synthetic_generics<'tcx>(
             let impl_def_id = impl_def_id.expect_local();
             let impl_span = tcx.def_span(impl_def_id);
             let trait_span = tcx.def_span(trait_def_id);
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 impl_span,
                 E0643,
@@ -1689,7 +1686,7 @@ fn compare_generic_param_kinds<'tcx>(
             let param_impl_span = tcx.def_span(param_impl.def_id);
             let param_trait_span = tcx.def_span(param_trait.def_id);
 
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 param_impl_span,
                 E0053,
@@ -1836,7 +1833,7 @@ fn compare_const_predicate_entailment<'tcx>(
         let (ty, _) = tcx.hir().expect_impl_item(impl_ct_def_id).expect_const();
         cause.span = ty.span;
 
-        let mut diag = struct_span_err!(
+        let mut diag = struct_span_code_err!(
             tcx.dcx(),
             cause.span,
             E0326,
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index fd1571426c8..f7fc0c81b95 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -7,7 +7,7 @@ use rustc_middle::traits::{ObligationCause, Reveal};
 use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperVisitable, TypeVisitable, TypeVisitor,
 };
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_trait_selection::traits::{
     elaborate, normalize_param_env_or_error, outlives_bounds::InferCtxtExt, ObligationCtxt,
 };
@@ -153,10 +153,7 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
         trait_m_sig.inputs_and_output,
     ));
     if !ocx.select_all_or_error().is_empty() {
-        tcx.dcx().span_delayed_bug(
-            DUMMY_SP,
-            "encountered errors when checking RPITIT refinement (selection)",
-        );
+        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
         return;
     }
     let outlives_env = OutlivesEnvironment::with_bounds(
@@ -165,18 +162,12 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
     );
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
-        tcx.dcx().span_delayed_bug(
-            DUMMY_SP,
-            "encountered errors when checking RPITIT refinement (regions)",
-        );
+        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
         return;
     }
     // Resolve any lifetime variables that may have been introduced during normalization.
     let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
-        tcx.dcx().span_delayed_bug(
-            DUMMY_SP,
-            "encountered errors when checking RPITIT refinement (resolution)",
-        );
+        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (resolution)");
         return;
     };
 
diff --git a/compiler/rustc_hir_analysis/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 3492499db68..3275a81c3dd 100644
--- a/compiler/rustc_hir_analysis/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -2,7 +2,7 @@
 //
 // We don't do any drop checking during hir typeck.
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_errors::{struct_span_code_err, ErrorGuaranteed};
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
 use rustc_infer::infer::{RegionResolutionError, TyCtxtInferExt};
 use rustc_middle::ty::util::CheckRegions;
@@ -88,8 +88,12 @@ fn ensure_drop_params_and_item_params_correspond<'tcx>(
     let drop_impl_span = tcx.def_span(drop_impl_did);
     let item_span = tcx.def_span(self_type_did);
     let self_descr = tcx.def_descr(self_type_did);
-    let mut err =
-        struct_span_err!(tcx.dcx(), drop_impl_span, E0366, "`Drop` impls cannot be specialized");
+    let mut err = struct_span_code_err!(
+        tcx.dcx(),
+        drop_impl_span,
+        E0366,
+        "`Drop` impls cannot be specialized"
+    );
     match arg {
         ty::util::NotUniqueParam::DuplicateParam(arg) => {
             err.note(format!("`{arg}` is mentioned multiple times"))
@@ -154,14 +158,14 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
                 let item_span = tcx.def_span(adt_def_id);
                 let self_descr = tcx.def_descr(adt_def_id.to_def_id());
                 guar = Some(
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         error.root_obligation.cause.span,
                         E0367,
                         "`Drop` impl requires `{root_predicate}` \
                         but the {self_descr} it is implemented for does not",
                     )
-                    .span_note(item_span, "the implementor must specify the same requirement")
+                    .with_span_note(item_span, "the implementor must specify the same requirement")
                     .emit(),
                 );
             }
@@ -186,14 +190,14 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
                 }
             };
             guar = Some(
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     error.origin().span(),
                     E0367,
                     "`Drop` impl requires `{outlives}` \
                     but the {self_descr} it is implemented for does not",
                 )
-                .span_note(item_span, "the implementor must specify the same requirement")
+                .with_span_note(item_span, "the implementor must specify the same requirement")
                 .emit(),
             );
         }
diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs
new file mode 100644
index 00000000000..27bb2c57a5c
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/errs.rs
@@ -0,0 +1,97 @@
+use rustc_hir as hir;
+use rustc_hir_pretty::qpath_to_string;
+use rustc_lint_defs::builtin::STATIC_MUT_REF;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::Span;
+use rustc_type_ir::Mutability;
+
+use crate::errors;
+
+/// Check for shared or mutable references of `static mut` inside expression
+pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
+    let span = expr.span;
+    let hir_id = expr.hir_id;
+    if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind
+        && matches!(borrow_kind, hir::BorrowKind::Ref)
+        && let Some(var) = is_path_static_mut(*expr)
+    {
+        handle_static_mut_ref(
+            tcx,
+            span,
+            var,
+            span.edition().at_least_rust_2024(),
+            matches!(m, Mutability::Mut),
+            hir_id,
+        );
+    }
+}
+
+/// Check for shared or mutable references of `static mut` inside statement
+pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
+    if let hir::StmtKind::Local(loc) = stmt.kind
+        && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
+        && matches!(ba.0, rustc_ast::ByRef::Yes)
+        && let Some(init) = loc.init
+        && let Some(var) = is_path_static_mut(*init)
+    {
+        handle_static_mut_ref(
+            tcx,
+            init.span,
+            var,
+            loc.span.edition().at_least_rust_2024(),
+            matches!(ba.1, Mutability::Mut),
+            stmt.hir_id,
+        );
+    }
+}
+
+fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
+    if let hir::ExprKind::Path(qpath) = expr.kind
+        && let hir::QPath::Resolved(_, path) = qpath
+        && let hir::def::Res::Def(def_kind, _) = path.res
+        && let hir::def::DefKind::Static(mt) = def_kind
+        && matches!(mt, Mutability::Mut)
+    {
+        return Some(qpath_to_string(&qpath));
+    }
+    None
+}
+
+fn handle_static_mut_ref(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    var: String,
+    e2024: bool,
+    mutable: bool,
+    hir_id: hir::HirId,
+) {
+    if e2024 {
+        let sugg = if mutable {
+            errors::StaticMutRefSugg::Mut { span, var }
+        } else {
+            errors::StaticMutRefSugg::Shared { span, var }
+        };
+        tcx.sess.parse_sess.dcx.emit_err(errors::StaticMutRef { span, sugg });
+        return;
+    }
+
+    let (label, sugg, shared) = if mutable {
+        (
+            errors::RefOfMutStaticLabel::Mut { span },
+            errors::RefOfMutStaticSugg::Mut { span, var },
+            "mutable ",
+        )
+    } else {
+        (
+            errors::RefOfMutStaticLabel::Shared { span },
+            errors::RefOfMutStaticSugg::Shared { span, var },
+            "shared ",
+        )
+    };
+    tcx.emit_spanned_lint(
+        STATIC_MUT_REF,
+        hir_id,
+        span,
+        errors::RefOfMutStatic { shared, why_note: (), label, sugg },
+    );
+}
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 632af780ed8..7c3e296dfce 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -8,7 +8,7 @@ use crate::errors::{
 };
 
 use hir::def_id::DefId;
-use rustc_errors::{struct_span_err, DiagnosticMessage};
+use rustc_errors::{struct_span_code_err, DiagnosticMessage};
 use rustc_hir as hir;
 use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
 use rustc_middle::ty::{self, Ty, TyCtxt};
@@ -29,8 +29,8 @@ fn equate_intrinsic_type<'tcx>(
             (own_counts, generics.span)
         }
         _ => {
-            struct_span_err!(tcx.dcx(), it.span, E0622, "intrinsic must be a function")
-                .span_label(it.span, "expected a function")
+            struct_span_code_err!(tcx.dcx(), it.span, E0622, "intrinsic must be a function")
+                .with_span_label(it.span, "expected a function")
                 .emit();
             return;
         }
@@ -552,7 +552,7 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
         sym::simd_shuffle_generic => (2, 1, vec![param(0), param(0)], param(1)),
         _ => {
             let msg = format!("unrecognized platform-specific intrinsic function: `{name}`");
-            tcx.dcx().struct_span_err(it.span, msg).emit();
+            tcx.dcx().span_err(it.span, msg);
             return;
         }
     };
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
index ac18e6de0ba..db619d5169e 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -4,7 +4,7 @@ use rustc_hir as hir;
 use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy};
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{Symbol, DUMMY_SP};
+use rustc_span::Symbol;
 use rustc_target::abi::FieldIdx;
 use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType};
 
@@ -153,12 +153,14 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         };
         let Some(asm_ty) = asm_ty else {
             let msg = format!("cannot use value of type `{ty}` for inline assembly");
-            let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
-            err.note(
-                "only integers, floats, SIMD vectors, pointers and function pointers \
-                 can be used as arguments for inline assembly",
-            );
-            err.emit();
+            self.tcx
+                .dcx()
+                .struct_span_err(expr.span, msg)
+                .with_note(
+                    "only integers, floats, SIMD vectors, pointers and function pointers \
+                     can be used as arguments for inline assembly",
+                )
+                .emit();
             return None;
         };
 
@@ -166,9 +168,11 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         // possibly fail is for SIMD types which don't #[derive(Copy)].
         if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
             let msg = "arguments for inline assembly must be copyable";
-            let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
-            err.note(format!("`{ty}` does not implement the Copy trait"));
-            err.emit();
+            self.tcx
+                .dcx()
+                .struct_span_err(expr.span, msg)
+                .with_note(format!("`{ty}` does not implement the Copy trait"))
+                .emit();
         }
 
         // Ideally we wouldn't need to do this, but LLVM's register allocator
@@ -183,16 +187,17 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         if let Some((in_expr, Some(in_asm_ty))) = tied_input {
             if in_asm_ty != asm_ty {
                 let msg = "incompatible types for asm inout argument";
-                let mut err = self.tcx.dcx().struct_span_err(vec![in_expr.span, expr.span], msg);
-
                 let in_expr_ty = (self.get_operand_ty)(in_expr);
-                err.span_label(in_expr.span, format!("type `{in_expr_ty}`"));
-                err.span_label(expr.span, format!("type `{ty}`"));
-                err.note(
-                    "asm inout arguments must have the same type, \
-                    unless they are both pointers or integers of the same size",
-                );
-                err.emit();
+                self.tcx
+                    .dcx()
+                    .struct_span_err(vec![in_expr.span, expr.span], msg)
+                    .with_span_label(in_expr.span, format!("type `{in_expr_ty}`"))
+                    .with_span_label(expr.span, format!("type `{ty}`"))
+                    .with_note(
+                        "asm inout arguments must have the same type, \
+                        unless they are both pointers or integers of the same size",
+                    )
+                    .emit();
             }
 
             // All of the later checks have already been done on the input, so
@@ -234,13 +239,15 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
         if let Some(feature) = feature {
             if !target_features.contains(feature) {
                 let msg = format!("`{feature}` target feature is not enabled");
-                let mut err = self.tcx.dcx().struct_span_err(expr.span, msg);
-                err.note(format!(
-                    "this is required to use type `{}` with register class `{}`",
-                    ty,
-                    reg_class.name(),
-                ));
-                err.emit();
+                self.tcx
+                    .dcx()
+                    .struct_span_err(expr.span, msg)
+                    .with_note(format!(
+                        "this is required to use type `{}` with register class `{}`",
+                        ty,
+                        reg_class.name(),
+                    ))
+                    .emit();
                 return Some(asm_ty);
             }
         }
@@ -287,7 +294,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
     pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: LocalDefId) {
         let target_features = self.tcx.asm_target_features(enclosing_id.to_def_id());
         let Some(asm_arch) = self.tcx.sess.asm_arch else {
-            self.tcx.dcx().span_delayed_bug(DUMMY_SP, "target architecture does not support asm");
+            self.tcx.dcx().delayed_bug("target architecture does not support asm");
             return;
         };
         for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
@@ -318,7 +325,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         op.is_clobber(),
                     ) {
                         let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
-                        self.tcx.dcx().struct_span_err(*op_sp, msg).emit();
+                        self.tcx.dcx().span_err(*op_sp, msg);
                         continue;
                     }
                 }
@@ -357,7 +364,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                                 reg_class.name(),
                                 feature
                             );
-                            self.tcx.dcx().struct_span_err(*op_sp, msg).emit();
+                            self.tcx.dcx().span_err(*op_sp, msg);
                             // register isn't enabled, don't do more checks
                             continue;
                         }
@@ -371,7 +378,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                                     .intersperse(", ")
                                     .collect::<String>(),
                             );
-                            self.tcx.dcx().struct_span_err(*op_sp, msg).emit();
+                            self.tcx.dcx().span_err(*op_sp, msg);
                             // register isn't enabled, don't do more checks
                             continue;
                         }
@@ -449,14 +456,17 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
                         ty::Never | ty::Error(_) => {}
                         ty::FnDef(..) => {}
                         _ => {
-                            let mut err =
-                                self.tcx.dcx().struct_span_err(*op_sp, "invalid `sym` operand");
-                            err.span_label(
-                                self.tcx.def_span(anon_const.def_id),
-                                format!("is {} `{}`", ty.kind().article(), ty),
-                            );
-                            err.help("`sym` operands must refer to either a function or a static");
-                            err.emit();
+                            self.tcx
+                                .dcx()
+                                .struct_span_err(*op_sp, "invalid `sym` operand")
+                                .with_span_label(
+                                    self.tcx.def_span(anon_const.def_id),
+                                    format!("is {} `{}`", ty.kind().article(), ty),
+                                )
+                                .with_help(
+                                    "`sym` operands must refer to either a function or a static",
+                                )
+                                .emit();
                         }
                     };
                 }
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index faec72cfeb6..3b05eaedf34 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -66,6 +66,7 @@ mod check;
 mod compare_impl_item;
 pub mod dropck;
 mod entry;
+mod errs;
 pub mod intrinsic;
 pub mod intrinsicck;
 mod region;
@@ -75,10 +76,9 @@ pub use check::check_abi;
 
 use std::num::NonZeroU32;
 
-use check::check_mod_item_types;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::ErrorGuaranteed;
-use rustc_errors::{pluralize, struct_span_err, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{pluralize, struct_span_code_err, Diagnostic, DiagnosticBuilder};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_index::bit_set::BitSet;
@@ -110,7 +110,6 @@ pub fn provide(providers: &mut Providers) {
     wfcheck::provide(providers);
     *providers = Providers {
         adt_destructor,
-        check_mod_item_types,
         region_scope_tree,
         collect_return_position_impl_trait_in_trait_tys,
         compare_impl_const: compare_impl_item::compare_impl_const_raw,
diff --git a/compiler/rustc_hir_analysis/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index eab83c7a254..5d5a4789734 100644
--- a/compiler/rustc_hir_analysis/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -18,6 +18,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::source_map;
 use rustc_span::Span;
 
+use super::errs::{maybe_expr_static_mut, maybe_stmt_static_mut};
+
 use std::mem;
 
 #[derive(Debug, Copy, Clone)]
@@ -177,6 +179,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
 }
 
 fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
+    fn has_let_expr(expr: &Expr<'_>) -> bool {
+        match &expr.kind {
+            hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+            hir::ExprKind::Let(..) => true,
+            _ => false,
+        }
+    }
+
     let prev_cx = visitor.cx;
 
     visitor.terminating_scopes.insert(arm.hir_id.local_id);
@@ -184,7 +194,9 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir
     visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
     visitor.cx.var_parent = visitor.cx.parent;
 
-    if let Some(hir::Guard::If(expr)) = arm.guard {
+    if let Some(expr) = arm.guard
+        && !has_let_expr(expr)
+    {
         visitor.terminating_scopes.insert(expr.hir_id.local_id);
     }
 
@@ -214,6 +226,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
     let stmt_id = stmt.hir_id.local_id;
     debug!("resolve_stmt(stmt.id={:?})", stmt_id);
 
+    maybe_stmt_static_mut(visitor.tcx, *stmt);
+
     // Every statement will clean up the temporaries created during
     // execution of that statement. Therefore each statement has an
     // associated destruction scope that represents the scope of the
@@ -232,6 +246,8 @@ fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx h
 fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
     debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr);
 
+    maybe_expr_static_mut(visitor.tcx, *expr);
+
     let prev_cx = visitor.cx;
     visitor.enter_node_scope_with_dtor(expr.hir_id.local_id);
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 580d4bd5b02..59c72227144 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -3,7 +3,9 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
 
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{
+    pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
 use rustc_hir::lang_items::LangItem;
@@ -172,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
         item.name = ? tcx.def_path_str(def_id)
     );
 
-    match item.kind {
+    let res = match item.kind {
         // Right now we check that every default trait implementation
         // has an implementation of itself. Basically, a case like:
         //
@@ -197,11 +199,12 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
             let mut res = Ok(());
             if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
                 let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
-                let mut err =
-                    tcx.dcx().struct_span_err(sp, "impls of auto traits cannot be default");
-                err.span_labels(impl_.defaultness_span, "default because of this");
-                err.span_label(sp, "auto trait");
-                res = Err(err.emit());
+                res = Err(tcx
+                    .dcx()
+                    .struct_span_err(sp, "impls of auto traits cannot be default")
+                    .with_span_labels(impl_.defaultness_span, "default because of this")
+                    .with_span_label(sp, "auto trait")
+                    .emit());
             }
             // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span.
             match tcx.impl_polarity(def_id) {
@@ -216,7 +219,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
                     if let hir::Defaultness::Default { .. } = impl_.defaultness {
                         let mut spans = vec![span];
                         spans.extend(impl_.defaultness_span);
-                        res = Err(struct_span_err!(
+                        res = Err(struct_span_code_err!(
                             tcx.dcx(),
                             spans,
                             E0750,
@@ -271,7 +274,11 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) -> Result<()
             }
         }
         _ => Ok(()),
-    }
+    };
+
+    crate::check::check::check_item_type(tcx, def_id);
+
+    res
 }
 
 fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) -> Result<(), ErrorGuaranteed> {
@@ -485,35 +492,32 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
 
         if !unsatisfied_bounds.is_empty() {
             let plural = pluralize!(unsatisfied_bounds.len());
-            let mut err = tcx.dcx().struct_span_err(
-                gat_item_hir.span,
-                format!("missing required bound{} on `{}`", plural, gat_item_hir.ident),
-            );
-
             let suggestion = format!(
                 "{} {}",
                 gat_item_hir.generics.add_where_or_trailing_comma(),
                 unsatisfied_bounds.join(", "),
             );
-            err.span_suggestion(
-                gat_item_hir.generics.tail_span_for_predicate_suggestion(),
-                format!("add the required where clause{plural}"),
-                suggestion,
-                Applicability::MachineApplicable,
-            );
-
             let bound =
                 if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" };
-            err.note(format!(
-                "{bound} currently required to ensure that impls have maximum flexibility"
-            ));
-            err.note(
-                "we are soliciting feedback, see issue #87479 \
-                 <https://github.com/rust-lang/rust/issues/87479> \
-                 for more information",
-            );
-
-            err.emit();
+            tcx.dcx()
+                .struct_span_err(
+                    gat_item_hir.span,
+                    format!("missing required bound{} on `{}`", plural, gat_item_hir.ident),
+                )
+                .with_span_suggestion(
+                    gat_item_hir.generics.tail_span_for_predicate_suggestion(),
+                    format!("add the required where clause{plural}"),
+                    suggestion,
+                    Applicability::MachineApplicable,
+                )
+                .with_note(format!(
+                    "{bound} currently required to ensure that impls have maximum flexibility"
+                ))
+                .with_note(
+                    "we are soliciting feedback, see issue #87479 \
+                     <https://github.com/rust-lang/rust/issues/87479> for more information",
+                )
+                .emit();
         }
     }
 }
@@ -834,8 +838,8 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
                 trait_should_be_self,
                 "associated item referring to unboxed trait object for its own trait",
             )
-            .span_label(trait_name.span, "in this trait")
-            .multipart_suggestion(
+            .with_span_label(trait_name.span, "in this trait")
+            .with_multipart_suggestion(
                 "you might have meant to use `Self` to refer to the implementing type",
                 sugg,
                 Applicability::MachineApplicable,
@@ -934,8 +938,8 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
                 };
                 if may_suggest_feature && tcx.sess.is_nightly_build() {
                     diag.help(
-                            "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
-                        );
+                        "add `#![feature(adt_const_params)]` to the crate attributes to enable more complex and user defined types",
+                    );
                 }
 
                 Err(diag.emit())
@@ -1113,7 +1117,7 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) -> Result<(), ErrorGuarant
         || matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
     {
         for associated_def_id in &*tcx.associated_item_def_ids(def_id) {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 tcx.def_span(*associated_def_id),
                 E0714,
@@ -1595,7 +1599,7 @@ fn check_method_receiver<'tcx>(
                          the `arbitrary_self_types` feature",
                     ),
                 )
-                .help(HELP_FOR_SELF_TYPE)
+                .with_help(HELP_FOR_SELF_TYPE)
                 .emit()
             } else {
                 // Report error; would not have worked with `arbitrary_self_types`.
@@ -1607,9 +1611,9 @@ fn check_method_receiver<'tcx>(
 }
 
 fn e0307(tcx: TyCtxt<'_>, span: Span, receiver_ty: Ty<'_>) -> ErrorGuaranteed {
-    struct_span_err!(tcx.dcx(), span, E0307, "invalid `self` parameter type: {receiver_ty}")
-        .note("type of `self` must be `Self` or a type that dereferences to it")
-        .help(HELP_FOR_SELF_TYPE)
+    struct_span_code_err!(tcx.dcx(), span, E0307, "invalid `self` parameter type: {receiver_ty}")
+        .with_note("type of `self` must be `Self` or a type that dereferences to it")
+        .with_help(HELP_FOR_SELF_TYPE)
         .emit()
 }
 
@@ -1909,14 +1913,16 @@ fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalModDefId) -> Result<(), Error
     let mut res = items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
     res = res.and(items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id)));
     res = res.and(items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id)));
-    res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)))
+    res = res.and(items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id)));
+    if module == LocalModDefId::CRATE_DEF_ID {
+        super::entry::check_for_entry_fn(tcx);
+    }
+    res
 }
 
 fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) -> DiagnosticBuilder<'_> {
-    let mut err =
-        struct_span_err!(tcx.dcx(), span, E0392, "parameter `{param_name}` is never used");
-    err.span_label(span, "unused parameter");
-    err
+    struct_span_code_err!(tcx.dcx(), span, E0392, "parameter `{param_name}` is never used")
+        .with_span_label(span, "unused parameter")
 }
 
 pub fn provide(providers: &mut Providers) {
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index ec15aa65e7a..4c3455c7240 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -70,17 +70,16 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
             match seen_items.entry(norm_ident) {
                 Entry::Occupied(entry) => {
                     let former = entry.get();
-                    let mut err = struct_span_err!(
+                    struct_span_code_err!(
                         self.tcx.dcx(),
                         span,
                         E0592,
                         "duplicate definitions with name `{}`",
                         ident,
-                    );
-                    err.span_label(span, format!("duplicate definitions for `{ident}`"));
-                    err.span_label(*former, format!("other definition for `{ident}`"));
-
-                    err.emit();
+                    )
+                    .with_span_label(span, format!("duplicate definitions for `{ident}`"))
+                    .with_span_label(*former, format!("other definition for `{ident}`"))
+                    .emit();
                 }
                 Entry::Vacant(entry) => {
                     entry.insert(span);
@@ -105,7 +104,7 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
 
             if let Some(item2) = collision {
                 let name = item1.ident(self.tcx).normalize_to_macros_2_0();
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     self.tcx.dcx(),
                     self.tcx.def_span(item1.def_id),
                     E0592,
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 5cc9da25d6a..561a254e89e 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -6,7 +6,7 @@
 // mappings. That mapping code resides here.
 
 use crate::errors;
-use rustc_errors::{error_code, struct_span_err};
+use rustc_errors::{error_code, struct_span_code_err};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
@@ -45,7 +45,7 @@ fn enforce_trait_manually_implementable(
     // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]`
     if tcx.trait_def(trait_def_id).deny_explicit_impl {
         let trait_name = tcx.item_name(trait_def_id);
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             tcx.dcx(),
             impl_header_span,
             E0322,
@@ -88,7 +88,7 @@ fn enforce_empty_impls_for_marker_traits(
         return;
     }
 
-    struct_span_err!(
+    struct_span_code_err!(
         tcx.dcx(),
         tcx.def_span(impl_def_id),
         E0715,
@@ -173,7 +173,7 @@ fn check_object_overlap<'tcx>(
                 let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id);
                 if supertrait_def_ids.any(|d| d == trait_def_id) {
                     let span = tcx.def_span(impl_def_id);
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         span,
                         E0371,
@@ -181,7 +181,7 @@ fn check_object_overlap<'tcx>(
                         trait_ref.self_ty(),
                         tcx.def_path_str(trait_def_id)
                     )
-                    .span_label(
+                    .with_span_label(
                         span,
                         format!(
                             "`{}` automatically implements trait `{}`",
diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
index d208c55055b..7b146573a1b 100644
--- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
@@ -1,7 +1,7 @@
 //! Unsafety checker: every impl either implements a trait defined in this
 //! crate or pertains to a type defined in this crate.
 
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::Unsafety;
 use rustc_middle::ty::TyCtxt;
@@ -18,14 +18,14 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
         match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
             (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     tcx.def_span(def_id),
                     E0199,
                     "implementing the trait `{}` is not unsafe",
                     trait_ref.print_trait_sugared()
                 )
-                .span_suggestion_verbose(
+                .with_span_suggestion_verbose(
                     item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)),
                     "remove `unsafe` from this trait implementation",
                     "",
@@ -35,20 +35,20 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             }
 
             (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     tcx.def_span(def_id),
                     E0200,
                     "the trait `{}` requires an `unsafe impl` declaration",
                     trait_ref.print_trait_sugared()
                 )
-                .note(format!(
+                .with_note(format!(
                     "the trait `{}` enforces invariants that the compiler can't check. \
                     Review the trait documentation and make sure this implementation \
                     upholds those invariants before adding the `unsafe` keyword",
                     trait_ref.print_trait_sugared()
                 ))
-                .span_suggestion_verbose(
+                .with_span_suggestion_verbose(
                     item.span.shrink_to_lo(),
                     "add `unsafe` to this trait implementation",
                     "unsafe ",
@@ -58,20 +58,20 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
             }
 
             (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     tcx.def_span(def_id),
                     E0569,
                     "requires an `unsafe impl` declaration due to `#[{}]` attribute",
                     attr_name
                 )
-                .note(format!(
+                .with_note(format!(
                     "the trait `{}` enforces invariants that the compiler can't check. \
                     Review the trait documentation and make sure this implementation \
                     upholds those invariants before adding the `unsafe` keyword",
                     trait_ref.print_trait_sugared()
                 ))
-                .span_suggestion_verbose(
+                .with_span_suggestion_verbose(
                     item.span.shrink_to_lo(),
                     "add `unsafe` to this trait implementation",
                     "unsafe ",
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 8aeab2ca67e..c9f89a0c3ef 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -16,6 +16,7 @@
 
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -641,7 +642,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
             tcx.ensure().generics_of(def_id);
             tcx.ensure().type_of(def_id);
             tcx.ensure().predicates_of(def_id);
-            if !is_suggestable_infer_ty(ty) {
+            if !ty.is_suggestable_infer_ty() {
                 let mut visitor = HirPlaceholderCollector::default();
                 visitor.visit_item(it);
                 placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
@@ -673,7 +674,7 @@ fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
         hir::TraitItemKind::Const(ty, body_id) => {
             tcx.ensure().type_of(def_id);
             if !tcx.dcx().has_stashed_diagnostic(ty.span, StashKey::ItemNoType)
-                && !(is_suggestable_infer_ty(ty) && body_id.is_some())
+                && !(ty.is_suggestable_infer_ty() && body_id.is_some())
             {
                 // Account for `const C: _;`.
                 let mut visitor = HirPlaceholderCollector::default();
@@ -725,7 +726,7 @@ fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
         }
         hir::ImplItemKind::Const(ty, _) => {
             // Account for `const T: _ = ..;`
-            if !is_suggestable_infer_ty(ty) {
+            if !ty.is_suggestable_infer_ty() {
                 let mut visitor = HirPlaceholderCollector::default();
                 visitor.visit_impl_item(impl_item);
                 placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
@@ -979,7 +980,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
         })
         // Check for duplicates
         .and_then(|list| {
-            let mut set: FxHashMap<Symbol, Span> = FxHashMap::default();
+            let mut set: UnordMap<Symbol, Span> = Default::default();
             let mut no_dups = true;
 
             for ident in &*list {
@@ -1053,48 +1054,6 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     }
 }
 
-fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool {
-    generic_args.iter().any(|arg| match arg {
-        hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty),
-        hir::GenericArg::Infer(_) => true,
-        _ => false,
-    })
-}
-
-/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to
-/// use inference to provide suggestions for the appropriate type if possible.
-fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
-    debug!(?ty);
-    use hir::TyKind::*;
-    match &ty.kind {
-        Infer => true,
-        Slice(ty) => is_suggestable_infer_ty(ty),
-        Array(ty, length) => {
-            is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _))
-        }
-        Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
-        Ptr(mut_ty) | Ref(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
-        OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args),
-        Path(hir::QPath::TypeRelative(ty, segment)) => {
-            is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args)
-        }
-        Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => {
-            ty_opt.is_some_and(is_suggestable_infer_ty)
-                || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args))
-        }
-        _ => false,
-    }
-}
-
-pub fn get_infer_ret_ty<'hir>(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> {
-    if let hir::FnRetTy::Return(ty) = output {
-        if is_suggestable_infer_ty(ty) {
-            return Some(*ty);
-        }
-    }
-    None
-}
-
 #[instrument(level = "debug", skip(tcx))]
 fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<ty::PolyFnSig<'_>> {
     use rustc_hir::Node::*;
@@ -1187,7 +1146,7 @@ fn infer_return_ty_for_fn_sig<'tcx>(
 ) -> ty::PolyFnSig<'tcx> {
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
 
-    match get_infer_ret_ty(&sig.decl.output) {
+    match sig.decl.output.get_infer_ret_ty() {
         Some(ty) => {
             let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
             // Typeck doesn't expect erased regions to be returned from `type_of`.
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 41520718aa8..ab9ed6ef98d 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
 use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{Span, DUMMY_SP};
 
 /// Returns a list of all type predicates (explicit and implicit) for the definition with
 /// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
@@ -38,38 +38,12 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
         // an obligation and instead be skipped. Otherwise we'd use
         // `tcx.def_span(def_id);`
         let span = rustc_span::DUMMY_SP;
-        let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) {
-            // when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound,
-            // because only implementing `Self: Trait<.., false>` is currently not possible.
-            Some((
-                ty::TraitRef::new(
-                    tcx,
-                    def_id,
-                    ty::GenericArgs::for_item(tcx, def_id, |param, _| {
-                        if param.is_host_effect() {
-                            tcx.consts.true_.into()
-                        } else {
-                            tcx.mk_param_from_def(param)
-                        }
-                    }),
-                )
-                .to_predicate(tcx),
+
+        result.predicates =
+            tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
+                ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
                 span,
-            ))
-        } else {
-            None
-        };
-        result.predicates = tcx.arena.alloc_from_iter(
-            result
-                .predicates
-                .iter()
-                .copied()
-                .chain(std::iter::once((
-                    ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
-                    span,
-                )))
-                .chain(non_const_bound),
-        );
+            ))));
     }
     debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
     result
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 9a28534d790..3d8390d1946 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -8,7 +8,7 @@
 
 use rustc_ast::walk_list;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
@@ -22,7 +22,7 @@ use rustc_middle::ty::{self, TyCtxt, TypeSuperVisitable, TypeVisitor};
 use rustc_session::lint;
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::{sym, Ident};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use std::fmt;
 
 use crate::errors;
@@ -335,13 +335,10 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
                     // though this may happen when we call `poly_trait_ref_binder_info` with
                     // an (erroneous, #113423) associated return type bound in an impl header.
                     if !supertrait_bound_vars.is_empty() {
-                        self.tcx.dcx().span_delayed_bug(
-                            DUMMY_SP,
-                            format!(
-                                "found supertrait lifetimes without a binder to append \
+                        self.tcx.dcx().delayed_bug(format!(
+                            "found supertrait lifetimes without a binder to append \
                                 them to: {supertrait_bound_vars:?}"
-                            ),
-                        );
+                        ));
                     }
                     break (vec![], BinderScopeType::Normal);
                 }
@@ -737,7 +734,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     // Ensure that the parent of the def is an item, not HRTB
                     let parent_id = self.tcx.hir().parent_id(hir_id);
                     if !parent_id.is_owner() {
-                        struct_span_err!(
+                        struct_span_code_err!(
                             self.tcx.dcx(),
                             lifetime.ident.span,
                             E0657,
@@ -750,12 +747,12 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                         kind: hir::ItemKind::OpaqueTy { .. }, ..
                     }) = self.tcx.hir_node(parent_id)
                     {
-                        let mut err = self.tcx.dcx().struct_span_err(
+                        self.tcx.dcx().struct_span_err(
                             lifetime.ident.span,
                             "higher kinded lifetime bounds on nested opaque types are not supported yet",
-                        );
-                        err.span_note(self.tcx.def_span(def_id), "lifetime declared here");
-                        err.emit();
+                        )
+                        .with_span_note(self.tcx.def_span(def_id), "lifetime declared here")
+                        .emit();
                         self.uninsert_lifetime_on_error(lifetime, def.unwrap());
                     }
                 }
@@ -2114,7 +2111,7 @@ pub fn deny_non_region_late_bound(
             hir::GenericParamKind::Lifetime { .. } => continue,
         };
 
-        let mut diag = tcx.dcx().struct_span_err(
+        let diag = tcx.dcx().struct_span_err(
             param.span,
             format!("late-bound {what} parameter not allowed on {where_}"),
         );
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 55720e6d2aa..3ceea3dc7ae 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -9,8 +9,8 @@ use rustc_middle::ty::{self, ImplTraitInTraitData, IsSuggestable, Ty, TyCtxt, Ty
 use rustc_span::symbol::Ident;
 use rustc_span::{Span, DUMMY_SP};
 
+use super::bad_placeholder;
 use super::ItemCtxt;
-use super::{bad_placeholder, is_suggestable_infer_ty};
 pub use opaque::test_opaque_hidden_types;
 
 mod opaque;
@@ -368,7 +368,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
             }
             TraitItemKind::Const(ty, body_id) => body_id
                 .and_then(|body_id| {
-                    is_suggestable_infer_ty(ty).then(|| {
+                    ty.is_suggestable_infer_ty().then(|| {
                         infer_placeholder_type(
                             tcx,
                             def_id,
@@ -392,7 +392,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
                 Ty::new_fn_def(tcx, def_id.to_def_id(), args)
             }
             ImplItemKind::Const(ty, body_id) => {
-                if is_suggestable_infer_ty(ty) {
+                if ty.is_suggestable_infer_ty() {
                     infer_placeholder_type(
                         tcx,
                         def_id,
@@ -416,7 +416,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
 
         Node::Item(item) => match item.kind {
             ItemKind::Static(ty, .., body_id) => {
-                if is_suggestable_infer_ty(ty) {
+                if ty.is_suggestable_infer_ty() {
                     infer_placeholder_type(
                         tcx,
                         def_id,
@@ -430,7 +430,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
                 }
             }
             ItemKind::Const(ty, _, body_id) => {
-                if is_suggestable_infer_ty(ty) {
+                if ty.is_suggestable_infer_ty() {
                     infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant")
                 } else {
                     icx.to_ty(ty)
@@ -603,6 +603,8 @@ fn infer_placeholder_type<'a>(
             }
 
             err.emit();
+            // diagnostic stashing loses the information of whether something is a hard error.
+            Ty::new_error_with_message(tcx, span, "ItemNoType is a hard error")
         }
         None => {
             let mut diag = bad_placeholder(tcx, vec![span], kind);
@@ -623,15 +625,9 @@ fn infer_placeholder_type<'a>(
                 }
             }
 
-            diag.emit();
+            Ty::new_error(tcx, diag.emit())
         }
     }
-
-    // Typeck doesn't expect erased regions to be returned from `type_of`.
-    tcx.fold_regions(ty, |r, _| match *r {
-        ty::ReErased => tcx.lifetimes.re_static,
-        _ => r,
-    })
 }
 
 fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) {
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
index 5a73097b0f6..1f7ca48234a 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs
@@ -69,6 +69,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local
             Node::Item(it) => locator.visit_item(it),
             Node::ImplItem(it) => locator.visit_impl_item(it),
             Node::TraitItem(it) => locator.visit_trait_item(it),
+            Node::ForeignItem(it) => locator.visit_foreign_item(it),
             other => bug!("{:?} is not a valid scope for an opaque type item", other),
         }
     }
@@ -133,6 +134,21 @@ impl TaitConstraintLocator<'_> {
             debug!("no constraint: no typeck results");
             return;
         }
+
+        if let Some(hir_sig) = self.tcx.hir_node_by_def_id(item_def_id).fn_decl() {
+            if hir_sig.output.get_infer_ret_ty().is_some() {
+                let guar = self.tcx.dcx().span_delayed_bug(
+                    hir_sig.output.span(),
+                    "inferring return types and opaque types do not mix well",
+                );
+                self.found = Some(ty::OpaqueHiddenType {
+                    span: DUMMY_SP,
+                    ty: Ty::new_error(self.tcx, guar),
+                });
+                return;
+            }
+        }
+
         // Calling `mir_borrowck` can lead to cycle errors through
         // const-checking, avoid calling it if we don't have to.
         // ```rust
@@ -240,6 +256,12 @@ impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> {
         self.check(it.owner_id.def_id);
         intravisit::walk_trait_item(self, it);
     }
+    fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
+        trace!(?it.owner_id);
+        assert_ne!(it.owner_id.def_id, self.def_id);
+        // No need to call `check`, as we do not run borrowck on foreign items.
+        intravisit::walk_foreign_item(self, it);
+    }
 }
 
 pub(super) fn find_opaque_ty_constraints_for_rpit<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 75e7a5524a7..4f22da4ba3b 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -319,10 +319,10 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for MissingTypeParams {
     #[track_caller]
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         let mut err = DiagnosticBuilder::new(dcx, level, fluent::hir_analysis_missing_type_params);
-        err.set_span(self.span);
+        err.span(self.span);
         err.code(error_code!(E0393));
-        err.set_arg("parameterCount", self.missing_type_params.len());
-        err.set_arg(
+        err.arg("parameterCount", self.missing_type_params.len());
+        err.arg(
             "parameters",
             self.missing_type_params
                 .iter()
@@ -1410,3 +1410,94 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
     pub mut_key: &'a str,
     pub ptr_ty: Ty<'a>,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_static_mut_ref, code = "E0796")]
+#[note]
+pub struct StaticMutRef {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: StaticMutRefSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub enum StaticMutRefSugg {
+    #[suggestion(
+        hir_analysis_suggestion,
+        style = "verbose",
+        code = "addr_of!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Shared {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    #[suggestion(
+        hir_analysis_suggestion_mut,
+        style = "verbose",
+        code = "addr_of_mut!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Mut {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+}
+
+// STATIC_MUT_REF lint
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_static_mut_ref_lint)]
+#[note]
+pub struct RefOfMutStatic<'a> {
+    pub shared: &'a str,
+    #[note(hir_analysis_why_note)]
+    pub why_note: (),
+    #[subdiagnostic]
+    pub label: RefOfMutStaticLabel,
+    #[subdiagnostic]
+    pub sugg: RefOfMutStaticSugg,
+}
+
+#[derive(Subdiagnostic)]
+pub enum RefOfMutStaticLabel {
+    #[label(hir_analysis_label)]
+    Shared {
+        #[primary_span]
+        span: Span,
+    },
+    #[label(hir_analysis_label_mut)]
+    Mut {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+#[derive(Subdiagnostic)]
+pub enum RefOfMutStaticSugg {
+    #[suggestion(
+        hir_analysis_suggestion,
+        style = "verbose",
+        code = "addr_of!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Shared {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+    #[suggestion(
+        hir_analysis_suggestion_mut,
+        style = "verbose",
+        code = "addr_of_mut!({var})",
+        applicability = "maybe-incorrect"
+    )]
+    Mut {
+        #[primary_span]
+        span: Span,
+        var: String,
+    },
+}
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index ff5fff9363f..3f9b1f384d7 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -12,7 +12,7 @@ use crate::constrained_generic_params as cgp;
 use min_specialization::check_min_specialization;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{LocalDefId, LocalModDefId};
 use rustc_middle::query::Providers;
@@ -170,7 +170,7 @@ fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId)
 }
 
 fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) {
-    let mut err = struct_span_err!(
+    let mut err = struct_span_code_err!(
         tcx.dcx(),
         span,
         E0207,
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 81d8982eb15..dfc54ac5b23 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -166,13 +166,12 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
 
     // this ensures that later parts of type checking can assume that items
     // have valid types and not error
-    // FIXME(matthewjasper) We shouldn't need to use `track_errors`.
-    tcx.sess.track_errors(|| {
-        tcx.sess.time("type_collecting", || {
-            tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
-        });
-    })?;
+    tcx.sess.time("type_collecting", || {
+        tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
+    });
 
+    // FIXME(matthewjasper) We shouldn't need to use `track_errors` anywhere in this function
+    // or the compiler in general.
     if tcx.features().rustc_attrs {
         tcx.sess.track_errors(|| {
             tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
@@ -200,18 +199,9 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
         })?;
     }
 
-    let errs = tcx.sess.time("wf_checking", || {
+    tcx.sess.time("wf_checking", || {
         tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
-    });
-
-    // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
-    tcx.sess.time("item_types_checking", || {
-        tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
-    });
-
-    // HACK: `check_mod_type_wf` may spuriously emit errors due to `span_delayed_bug`, even if
-    // those errors only actually get emitted in `check_mod_item_types`.
-    errs?;
+    })?;
 
     if tcx.features().rustc_attrs {
         tcx.sess.track_errors(|| collect::test_opaque_hidden_types(tcx))?;
diff --git a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index c17925471d9..0cb38094cec 100644
--- a/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -59,6 +59,17 @@ pub(super) fn infer_predicates(
                     }
                 }
 
+                DefKind::TyAlias if tcx.type_alias_is_lazy(item_did) => {
+                    insert_required_predicates_to_be_wf(
+                        tcx,
+                        tcx.type_of(item_did).instantiate_identity(),
+                        tcx.def_span(item_did),
+                        &global_inferred_outlives,
+                        &mut item_required_predicates,
+                        &mut explicit_map,
+                    );
+                }
+
                 _ => {}
             };
 
@@ -88,14 +99,14 @@ pub(super) fn infer_predicates(
 
 fn insert_required_predicates_to_be_wf<'tcx>(
     tcx: TyCtxt<'tcx>,
-    field_ty: Ty<'tcx>,
-    field_span: Span,
+    ty: Ty<'tcx>,
+    span: Span,
     global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
     required_predicates: &mut RequiredPredicates<'tcx>,
     explicit_map: &mut ExplicitPredicatesMap<'tcx>,
 ) {
-    for arg in field_ty.walk() {
-        let ty = match arg.unpack() {
+    for arg in ty.walk() {
+        let leaf_ty = match arg.unpack() {
             GenericArgKind::Type(ty) => ty,
 
             // No predicates from lifetimes or constants, except potentially
@@ -103,63 +114,26 @@ fn insert_required_predicates_to_be_wf<'tcx>(
             GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
         };
 
-        match *ty.kind() {
-            // The field is of type &'a T which means that we will have
-            // a predicate requirement of T: 'a (T outlives 'a).
-            //
-            // We also want to calculate potential predicates for the T
+        match *leaf_ty.kind() {
             ty::Ref(region, rty, _) => {
+                // The type is `&'a T` which means that we will have
+                // a predicate requirement of `T: 'a` (`T` outlives `'a`).
+                //
+                // We also want to calculate potential predicates for the `T`.
                 debug!("Ref");
-                insert_outlives_predicate(tcx, rty.into(), region, field_span, required_predicates);
+                insert_outlives_predicate(tcx, rty.into(), region, span, required_predicates);
             }
 
-            // For each Adt (struct/enum/union) type `Foo<'a, T>`, we
-            // can load the current set of inferred and explicit
-            // predicates from `global_inferred_outlives` and filter the
-            // ones that are TypeOutlives.
             ty::Adt(def, args) => {
-                // First check the inferred predicates
-                //
-                // Example 1:
-                //
-                //     struct Foo<'a, T> {
-                //         field1: Bar<'a, T>
-                //     }
-                //
-                //     struct Bar<'b, U> {
-                //         field2: &'b U
-                //     }
-                //
-                // Here, when processing the type of `field1`, we would
-                // request the set of implicit predicates computed for `Bar`
-                // thus far. This will initially come back empty, but in next
-                // round we will get `U: 'b`. We then apply the substitution
-                // `['b => 'a, U => T]` and thus get the requirement that `T:
-                // 'a` holds for `Foo`.
+                // For ADTs (structs/enums/unions), we check inferred and explicit predicates.
                 debug!("Adt");
-                if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) {
-                    for (unsubstituted_predicate, &span) in
-                        unsubstituted_predicates.as_ref().skip_binder()
-                    {
-                        // `unsubstituted_predicate` is `U: 'b` in the
-                        // example above. So apply the substitution to
-                        // get `T: 'a` (or `predicate`):
-                        let predicate = unsubstituted_predicates
-                            .rebind(*unsubstituted_predicate)
-                            .instantiate(tcx, args);
-                        insert_outlives_predicate(
-                            tcx,
-                            predicate.0,
-                            predicate.1,
-                            span,
-                            required_predicates,
-                        );
-                    }
-                }
-
-                // Check if the type has any explicit predicates that need
-                // to be added to `required_predicates`
-                // let _: () = args.region_at(0);
+                check_inferred_predicates(
+                    tcx,
+                    def.did(),
+                    args,
+                    global_inferred_outlives,
+                    required_predicates,
+                );
                 check_explicit_predicates(
                     tcx,
                     def.did(),
@@ -170,13 +144,31 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 );
             }
 
+            ty::Alias(ty::Weak, alias) => {
+                // This corresponds to a type like `Type<'a, T>`.
+                // We check inferred and explicit predicates.
+                debug!("Weak");
+                check_inferred_predicates(
+                    tcx,
+                    alias.def_id,
+                    alias.args,
+                    global_inferred_outlives,
+                    required_predicates,
+                );
+                check_explicit_predicates(
+                    tcx,
+                    alias.def_id,
+                    alias.args,
+                    required_predicates,
+                    explicit_map,
+                    None,
+                );
+            }
+
             ty::Dynamic(obj, ..) => {
                 // This corresponds to `dyn Trait<..>`. In this case, we should
                 // use the explicit predicates as well.
-
                 debug!("Dynamic");
-                debug!("field_ty = {}", &field_ty);
-                debug!("ty in field = {}", &ty);
                 if let Some(ex_trait_ref) = obj.principal() {
                     // Here, we are passing the type `usize` as a
                     // placeholder value with the function
@@ -198,21 +190,22 @@ fn insert_required_predicates_to_be_wf<'tcx>(
                 }
             }
 
-            ty::Alias(ty::Projection, obj) => {
-                // This corresponds to `<T as Foo<'a>>::Bar`. In this case, we should use the
-                // explicit predicates as well.
+            ty::Alias(ty::Projection, alias) => {
+                // This corresponds to a type like `<() as Trait<'a, T>>::Type`.
+                // We only use the explicit predicates of the trait but
+                // not the ones of the associated type itself.
                 debug!("Projection");
                 check_explicit_predicates(
                     tcx,
-                    tcx.parent(obj.def_id),
-                    obj.args,
+                    tcx.parent(alias.def_id),
+                    alias.args,
                     required_predicates,
                     explicit_map,
                     None,
                 );
             }
 
-            // FIXME(inherent_associated_types): Handle this case properly.
+            // FIXME(inherent_associated_types): Use the explicit predicates from the parent impl.
             ty::Alias(ty::Inherent, _) => {}
 
             _ => {}
@@ -220,19 +213,21 @@ fn insert_required_predicates_to_be_wf<'tcx>(
     }
 }
 
-/// We also have to check the explicit predicates
-/// declared on the type.
+/// Check the explicit predicates declared on the type.
+///
+/// ### Example
+///
 /// ```ignore (illustrative)
-/// struct Foo<'a, T> {
-///     field1: Bar<T>
+/// struct Outer<'a, T> {
+///     field: Inner<T>,
 /// }
 ///
-/// struct Bar<U> where U: 'static, U: Foo {
-///     ...
+/// struct Inner<U> where U: 'static, U: Outer {
+///     // ...
 /// }
 /// ```
 /// Here, we should fetch the explicit predicates, which
-/// will give us `U: 'static` and `U: Foo`. The latter we
+/// will give us `U: 'static` and `U: Outer`. The latter we
 /// can ignore, but we will want to process `U: 'static`,
 /// applying the substitution as above.
 fn check_explicit_predicates<'tcx>(
@@ -303,3 +298,45 @@ fn check_explicit_predicates<'tcx>(
         insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates);
     }
 }
+
+/// Check the inferred predicates declared on the type.
+///
+/// ### Example
+///
+/// ```ignore (illustrative)
+/// struct Outer<'a, T> {
+///     outer: Inner<'a, T>,
+/// }
+///
+/// struct Inner<'b, U> {
+///     inner: &'b U,
+/// }
+/// ```
+///
+/// Here, when processing the type of field `outer`, we would request the
+/// set of implicit predicates computed for `Inner` thus far. This will
+/// initially come back empty, but in next round we will get `U: 'b`.
+/// We then apply the substitution `['b => 'a, U => T]` and thus get the
+/// requirement that `T: 'a` holds for `Outer`.
+fn check_inferred_predicates<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    args: ty::GenericArgsRef<'tcx>,
+    global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>,
+    required_predicates: &mut RequiredPredicates<'tcx>,
+) {
+    // Load the current set of inferred and explicit predicates from `global_inferred_outlives`
+    // and filter the ones that are `TypeOutlives`.
+
+    let Some(predicates) = global_inferred_outlives.get(&def_id) else {
+        return;
+    };
+
+    for (&predicate, &span) in predicates.as_ref().skip_binder() {
+        // `predicate` is `U: 'b` in the example above.
+        // So apply the substitution to get `T: 'a`.
+        let ty::OutlivesPredicate(arg, region) =
+            predicates.rebind(predicate).instantiate(tcx, args);
+        insert_outlives_predicate(tcx, arg, region, span, required_predicates);
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
index 72511bfa01f..a87112dcc12 100644
--- a/compiler/rustc_hir_analysis/src/outlives/mod.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -21,6 +21,10 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
             let crate_map = tcx.inferred_outlives_crate(());
             crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
         }
+        DefKind::TyAlias if tcx.type_alias_is_lazy(item_def_id) => {
+            let crate_map = tcx.inferred_outlives_crate(());
+            crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[])
+        }
         DefKind::AnonConst if tcx.features().generic_const_exprs => {
             let id = tcx.local_def_id_to_hir_id(item_def_id);
             if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
@@ -47,8 +51,8 @@ fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clau
 }
 
 fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
-    // Compute a map from each struct/enum/union S to the **explicit**
-    // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
+    // Compute a map from each ADT (struct/enum/union) and lazy type alias to
+    // the **explicit** outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
     // Typically there won't be many of these, except in older code where
     // they were mandatory. Nonetheless, we have to ensure that every such
     // predicate is satisfied, so they form a kind of base set of requirements
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index ae68a8bf281..6657e3fd872 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -523,8 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
         let span = self.path_segment.ident.span;
         let msg = self.create_error_message();
-
-        self.tcx.dcx().struct_span_err_with_code(span, msg, self.code())
+        self.tcx.dcx().struct_span_err(span, msg).with_code(self.code())
     }
 
     /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index d6eea07cfbc..1eaec997053 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -1874,17 +1874,9 @@ impl<'a> State<'a> {
         self.print_pat(arm.pat);
         self.space();
         if let Some(ref g) = arm.guard {
-            match *g {
-                hir::Guard::If(e) => {
-                    self.word_space("if");
-                    self.print_expr(e);
-                    self.space();
-                }
-                hir::Guard::IfLet(&hir::Let { pat, ty, init, .. }) => {
-                    self.word_nbsp("if");
-                    self.print_let(pat, ty, init);
-                }
-            }
+            self.word_space("if");
+            self.print_expr(g);
+            self.space();
         }
         self.word_space("=>");
 
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 181de372840..cf1f232229d 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -78,16 +78,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut other_arms = vec![]; // Used only for diagnostics.
         let mut prior_arm = None;
         for arm in arms {
-            if let Some(g) = &arm.guard {
+            if let Some(e) = &arm.guard {
                 self.diverges.set(Diverges::Maybe);
-                match g {
-                    hir::Guard::If(e) => {
-                        self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
-                    }
-                    hir::Guard::IfLet(l) => {
-                        self.check_expr_let(l);
-                    }
-                };
+                self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
             }
 
             self.diverges.set(Diverges::Maybe);
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index de2cb5a6d5c..1a4e03d50ca 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -402,7 +402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 callee_expr.span,
                                 format!("evaluate({predicate:?}) = {result:?}"),
                             )
-                            .span_label(predicate_span, "predicate")
+                            .with_span_label(predicate_span, "predicate")
                             .emit();
                     }
                 }
@@ -490,11 +490,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.tcx.require_lang_item(hir::LangItem::FnOnce, Some(span));
                     let fn_once_output_def_id =
                         self.tcx.require_lang_item(hir::LangItem::FnOnceOutput, Some(span));
-                    if self.tcx.generics_of(fn_once_def_id).host_effect_index.is_none() {
-                        if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
-                            self.dcx().emit_err(errors::ConstSelectMustBeConst { span });
-                        }
-                    } else {
+                    if self.tcx.has_host_param(fn_once_def_id) {
                         let const_param: ty::GenericArg<'tcx> =
                             ([self.tcx.consts.false_, self.tcx.consts.true_])[idx].into();
                         self.register_predicate(traits::Obligation::new(
@@ -523,6 +519,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ));
 
                         self.select_obligations_where_possible(|_| {});
+                    } else if idx == 0 && !self.tcx.is_const_fn_raw(def_id) {
+                        self.dcx().emit_err(errors::ConstSelectMustBeConst { span });
                     }
                 } else {
                     self.dcx().emit_err(errors::ConstSelectMustBeFn { span, ty: arg_ty });
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 63185444479..a720a858f3c 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -269,7 +269,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             }
             CastError::NeedViaInt => {
                 make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx)
-                    .help("cast through an integer first")
+                    .with_help("cast through an integer first")
                     .emit();
             }
             CastError::IllegalCast => {
@@ -277,7 +277,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             }
             CastError::DifferingKinds => {
                 make_invalid_casting_error(self.span, self.expr_ty, self.cast_ty, fcx)
-                    .note("vtable kinds may not match")
+                    .with_note("vtable kinds may not match")
                     .emit();
             }
             CastError::CastToBool => {
@@ -512,7 +512,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                     self.cast_ty,
                     fcx,
                 )
-                .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate")
+                .with_note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate")
                 .emit();
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 2a408ac255c..c887368b2a2 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -28,10 +28,10 @@ use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
 pub(super) fn check_fn<'a, 'tcx>(
     fcx: &mut FnCtxt<'a, 'tcx>,
     fn_sig: ty::FnSig<'tcx>,
+    coroutine_types: Option<CoroutineTypes<'tcx>>,
     decl: &'tcx hir::FnDecl<'tcx>,
     fn_def_id: LocalDefId,
     body: &'tcx hir::Body<'tcx>,
-    closure_kind: Option<hir::ClosureKind>,
     params_can_be_unsized: bool,
 ) -> Option<CoroutineTypes<'tcx>> {
     let fn_id = fcx.tcx.local_def_id_to_hir_id(fn_def_id);
@@ -49,54 +49,13 @@ pub(super) fn check_fn<'a, 'tcx>(
             fcx.param_env,
         ));
 
+    fcx.coroutine_types = coroutine_types;
     fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
 
     let span = body.value.span;
 
     forbid_intrinsic_abi(tcx, span, fn_sig.abi);
 
-    if let Some(hir::ClosureKind::Coroutine(kind)) = closure_kind {
-        let yield_ty = match kind {
-            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
-            | hir::CoroutineKind::Coroutine(_) => {
-                let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::TypeInference,
-                    span,
-                });
-                fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
-                yield_ty
-            }
-            // HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly
-            // guide inference on the yield type so that we can handle `AsyncIterator`
-            // in this block in projection correctly. In the new trait solver, it is
-            // not a problem.
-            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
-                let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::TypeInference,
-                    span,
-                });
-                fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
-
-                Ty::new_adt(
-                    tcx,
-                    tcx.adt_def(tcx.require_lang_item(hir::LangItem::Poll, Some(span))),
-                    tcx.mk_args(&[Ty::new_adt(
-                        tcx,
-                        tcx.adt_def(tcx.require_lang_item(hir::LangItem::Option, Some(span))),
-                        tcx.mk_args(&[yield_ty.into()]),
-                    )
-                    .into()]),
-                )
-            }
-            hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => Ty::new_unit(tcx),
-        };
-
-        // Resume type defaults to `()` if the coroutine has no argument.
-        let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| Ty::new_unit(tcx));
-
-        fcx.resume_yield_tys = Some((resume_ty, yield_ty));
-    }
-
     GatherLocalsVisitor::new(fcx).visit_body(body);
 
     // C-variadic fns also have a `VaList` input that's not listed in `fn_sig`
@@ -114,7 +73,8 @@ pub(super) fn check_fn<'a, 'tcx>(
     let inputs_fn = fn_sig.inputs().iter().copied();
     for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() {
         // Check the pattern.
-        let ty_span = try { inputs_hir?.get(idx)?.span };
+        let ty: Option<&hir::Ty<'_>> = try { inputs_hir?.get(idx)? };
+        let ty_span = ty.map(|ty| ty.span);
         fcx.check_pat_top(param.pat, param_ty, ty_span, None, None);
 
         // Check that argument is Sized.
@@ -122,14 +82,14 @@ pub(super) fn check_fn<'a, 'tcx>(
             fcx.require_type_is_sized(
                 param_ty,
                 param.pat.span,
-                // ty_span == binding_span iff this is a closure parameter with no type ascription,
+                // ty.span == binding_span iff this is a closure parameter with no type ascription,
                 // or if it's an implicit `self` parameter
                 traits::SizedArgumentType(
                     if ty_span == Some(param.span) && tcx.is_closure_or_coroutine(fn_def_id.into())
                     {
                         None
                     } else {
-                        ty_span
+                        ty.map(|ty| ty.hir_id)
                     },
                 ),
             );
@@ -147,25 +107,6 @@ pub(super) fn check_fn<'a, 'tcx>(
     fcx.require_type_is_sized(declared_ret_ty, return_or_body_span, traits::SizedReturnType);
     fcx.check_return_expr(body.value, false);
 
-    // We insert the deferred_coroutine_interiors entry after visiting the body.
-    // This ensures that all nested coroutines appear before the entry of this coroutine.
-    // resolve_coroutine_interiors relies on this property.
-    let coroutine_ty = if let Some(hir::ClosureKind::Coroutine(coroutine_kind)) = closure_kind {
-        let interior = fcx
-            .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
-        fcx.deferred_coroutine_interiors.borrow_mut().push((
-            fn_def_id,
-            body.id(),
-            interior,
-            coroutine_kind,
-        ));
-
-        let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
-        Some(CoroutineTypes { resume_ty, yield_ty, interior })
-    } else {
-        None
-    };
-
     // Finalize the return check by taking the LUB of the return types
     // we saw and assigning it to the expected return type. This isn't
     // really expected to fail, since the coercions would have failed
@@ -201,7 +142,7 @@ pub(super) fn check_fn<'a, 'tcx>(
         check_lang_start_fn(tcx, fn_sig, fn_def_id);
     }
 
-    coroutine_ty
+    fcx.coroutine_types
 }
 
 fn check_panic_info_fn(tcx: TyCtxt<'_>, fn_id: LocalDefId, fn_sig: ty::FnSig<'_>) {
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index bf6fda20df8..7edb5912dd5 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -72,7 +72,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         opt_kind: Option<ty::ClosureKind>,
         expected_sig: Option<ExpectedSig<'tcx>>,
     ) -> Ty<'tcx> {
-        let body = self.tcx.hir().body(closure.body);
+        let tcx = self.tcx;
+        let body = tcx.hir().body(closure.body);
 
         trace!("decl = {:#?}", closure.fn_decl);
         let expr_def_id = closure.def_id;
@@ -83,81 +84,151 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         debug!(?bound_sig, ?liberated_sig);
 
+        // FIXME: We could probably actually just unify this further --
+        // instead of having a `FnSig` and a `Option<CoroutineTypes>`,
+        // we can have a `ClosureSignature { Coroutine { .. }, Closure { .. } }`,
+        // similar to how `ty::GenSig` is a distinct data structure.
+        let coroutine_types = match closure.kind {
+            hir::ClosureKind::Closure => None,
+            hir::ClosureKind::Coroutine(kind) => {
+                let yield_ty = match kind {
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
+                    | hir::CoroutineKind::Coroutine(_) => {
+                        let yield_ty = self.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span: expr_span,
+                        });
+                        self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
+                        yield_ty
+                    }
+                    // HACK(-Ztrait-solver=next): In the *old* trait solver, we must eagerly
+                    // guide inference on the yield type so that we can handle `AsyncIterator`
+                    // in this block in projection correctly. In the new trait solver, it is
+                    // not a problem.
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
+                        let yield_ty = self.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span: expr_span,
+                        });
+                        self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
+
+                        Ty::new_adt(
+                            tcx,
+                            tcx.adt_def(
+                                tcx.require_lang_item(hir::LangItem::Poll, Some(expr_span)),
+                            ),
+                            tcx.mk_args(&[Ty::new_adt(
+                                tcx,
+                                tcx.adt_def(
+                                    tcx.require_lang_item(hir::LangItem::Option, Some(expr_span)),
+                                ),
+                                tcx.mk_args(&[yield_ty.into()]),
+                            )
+                            .into()]),
+                        )
+                    }
+                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
+                        tcx.types.unit
+                    }
+                };
+
+                // Resume type defaults to `()` if the coroutine has no argument.
+                let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit);
+
+                Some(CoroutineTypes { resume_ty, yield_ty })
+            }
+        };
+
         let mut fcx = FnCtxt::new(self, self.param_env, closure.def_id);
-        let coroutine_types = check_fn(
+        check_fn(
             &mut fcx,
             liberated_sig,
+            coroutine_types,
             closure.fn_decl,
             expr_def_id,
             body,
-            Some(closure.kind),
             // Closure "rust-call" ABI doesn't support unsized params
             false,
         );
 
-        let parent_args = GenericArgs::identity_for_item(
-            self.tcx,
-            self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
-        );
+        let parent_args =
+            GenericArgs::identity_for_item(tcx, tcx.typeck_root_def_id(expr_def_id.to_def_id()));
 
         let tupled_upvars_ty = self.next_root_ty_var(TypeVariableOrigin {
             kind: TypeVariableOriginKind::ClosureSynthetic,
-            span: self.tcx.def_span(expr_def_id),
-        });
-
-        if let Some(CoroutineTypes { resume_ty, yield_ty, interior }) = coroutine_types {
-            let coroutine_args = ty::CoroutineArgs::new(
-                self.tcx,
-                ty::CoroutineArgsParts {
-                    parent_args,
-                    resume_ty,
-                    yield_ty,
-                    return_ty: liberated_sig.output(),
-                    witness: interior,
-                    tupled_upvars_ty,
-                },
-            );
-
-            return Ty::new_coroutine(self.tcx, expr_def_id.to_def_id(), coroutine_args.args);
-        }
-
-        // Tuple up the arguments and insert the resulting function type into
-        // the `closures` table.
-        let sig = bound_sig.map_bound(|sig| {
-            self.tcx.mk_fn_sig(
-                [Ty::new_tup(self.tcx, sig.inputs())],
-                sig.output(),
-                sig.c_variadic,
-                sig.unsafety,
-                sig.abi,
-            )
+            span: expr_span,
         });
 
-        debug!(?sig, ?opt_kind);
-
-        let closure_kind_ty = match opt_kind {
-            Some(kind) => Ty::from_closure_kind(self.tcx, kind),
+        match closure.kind {
+            hir::ClosureKind::Closure => {
+                assert_eq!(coroutine_types, None);
+                // Tuple up the arguments and insert the resulting function type into
+                // the `closures` table.
+                let sig = bound_sig.map_bound(|sig| {
+                    tcx.mk_fn_sig(
+                        [Ty::new_tup(tcx, sig.inputs())],
+                        sig.output(),
+                        sig.c_variadic,
+                        sig.unsafety,
+                        sig.abi,
+                    )
+                });
 
-            // Create a type variable (for now) to represent the closure kind.
-            // It will be unified during the upvar inference phase (`upvar.rs`)
-            None => self.next_root_ty_var(TypeVariableOrigin {
-                // FIXME(eddyb) distinguish closure kind inference variables from the rest.
-                kind: TypeVariableOriginKind::ClosureSynthetic,
-                span: expr_span,
-            }),
-        };
+                debug!(?sig, ?opt_kind);
+
+                let closure_kind_ty = match opt_kind {
+                    Some(kind) => Ty::from_closure_kind(tcx, kind),
+
+                    // Create a type variable (for now) to represent the closure kind.
+                    // It will be unified during the upvar inference phase (`upvar.rs`)
+                    None => self.next_root_ty_var(TypeVariableOrigin {
+                        // FIXME(eddyb) distinguish closure kind inference variables from the rest.
+                        kind: TypeVariableOriginKind::ClosureSynthetic,
+                        span: expr_span,
+                    }),
+                };
+
+                let closure_args = ty::ClosureArgs::new(
+                    tcx,
+                    ty::ClosureArgsParts {
+                        parent_args,
+                        closure_kind_ty,
+                        closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(tcx, sig),
+                        tupled_upvars_ty,
+                    },
+                );
 
-        let closure_args = ty::ClosureArgs::new(
-            self.tcx,
-            ty::ClosureArgsParts {
-                parent_args,
-                closure_kind_ty,
-                closure_sig_as_fn_ptr_ty: Ty::new_fn_ptr(self.tcx, sig),
-                tupled_upvars_ty,
-            },
-        );
+                Ty::new_closure(tcx, expr_def_id.to_def_id(), closure_args.args)
+            }
+            hir::ClosureKind::Coroutine(_) => {
+                let Some(CoroutineTypes { resume_ty, yield_ty }) = coroutine_types else {
+                    bug!("expected coroutine to have yield/resume types");
+                };
+                let interior = fcx.next_ty_var(TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::MiscVariable,
+                    span: body.value.span,
+                });
+                fcx.deferred_coroutine_interiors.borrow_mut().push((
+                    expr_def_id,
+                    body.id(),
+                    interior,
+                ));
+
+                let coroutine_args = ty::CoroutineArgs::new(
+                    tcx,
+                    ty::CoroutineArgsParts {
+                        parent_args,
+                        resume_ty,
+                        yield_ty,
+                        return_ty: liberated_sig.output(),
+                        witness: interior,
+                        tupled_upvars_ty,
+                    },
+                );
 
-        Ty::new_closure(self.tcx, expr_def_id.to_def_id(), closure_args.args)
+                Ty::new_coroutine(tcx, expr_def_id.to_def_id(), coroutine_args.args)
+            }
+        }
     }
 
     /// Given the expected type, figures out what it can about this closure we
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 1aa25a5bd7f..0b266202b26 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -36,7 +36,7 @@
 //! ```
 
 use crate::FnCtxt;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -1571,7 +1571,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                 let mut visitor = CollectRetsVisitor { ret_exprs: vec![] };
                 match *cause.code() {
                     ObligationCauseCode::ReturnNoExpression => {
-                        err = struct_span_err!(
+                        err = struct_span_code_err!(
                             fcx.dcx(),
                             cause.span,
                             E0069,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index c717319e507..9850892bd36 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -158,7 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Requires that the two types unify, and prints an error message if
     /// they don't.
     pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) {
+        if let Some(e) = self.demand_suptype_diag(sp, expected, actual) {
             e.emit();
         }
     }
@@ -189,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) {
-        if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) {
+        if let Some(err) = self.demand_eqtype_diag(sp, expected, actual) {
             err.emit();
         }
     }
@@ -228,7 +228,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Ty<'tcx> {
         let (ty, err) =
             self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase);
-        if let Some(mut err) = err {
+        if let Some(err) = err {
             err.emit();
         }
         ty
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index ff03cf16a27..4f929068887 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -215,7 +215,7 @@ impl AddToDiagnostic for TypeMismatchFruTypo {
     where
         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
-        diag.set_arg("expr", self.expr.as_deref().unwrap_or("NONE"));
+        diag.arg("expr", self.expr.as_deref().unwrap_or("NONE"));
 
         // Only explain that `a ..b` is a range if it's split up
         if self.expr_span.between(self.fru_span).is_empty() {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 7425d6f8901..fdad998c451 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -15,6 +15,7 @@ use crate::errors::{
 use crate::fatally_break_rust;
 use crate::method::SelfSource;
 use crate::type_error_struct;
+use crate::CoroutineTypes;
 use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
 use crate::{
     report_unexpected_variant_res, BreakableCtxt, Diverges, FnCtxt, Needs,
@@ -24,7 +25,7 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    pluralize, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder,
+    pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder,
     DiagnosticId, ErrorGuaranteed, StashKey,
 };
 use rustc_hir as hir;
@@ -939,12 +940,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
-        // FIXME: Make this use Diagnostic once error codes can be dynamically set.
-        let mut err = self.dcx().struct_span_err_with_code(
-            op_span,
-            "invalid left-hand side of assignment",
-            DiagnosticId::Error(err_code.into()),
-        );
+        let mut err = self.dcx().struct_span_err(op_span, "invalid left-hand side of assignment");
+        err.code(DiagnosticId::Error(err_code.into()));
         err.span_label(lhs.span, "cannot assign to this expression");
 
         self.comes_from_while_condition(lhs.hir_id, |expr| {
@@ -1337,7 +1334,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             Err(error) => {
                 if segment.ident.name != kw::Empty {
-                    if let Some(mut err) = self.report_method_error(
+                    if let Some(err) = self.report_method_error(
                         span,
                         rcvr_t,
                         segment.ident,
@@ -1763,7 +1760,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Make sure the programmer specified correct number of fields.
         if adt_kind == AdtKind::Union {
             if ast_fields.len() != 1 {
-                struct_span_err!(
+                struct_span_code_err!(
                     tcx.dcx(),
                     span,
                     E0784,
@@ -1970,7 +1967,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         };
 
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0063,
@@ -2010,7 +2007,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 != range_def_id
         {
             // Suppress any range expr type mismatches
-            if let Some(mut diag) =
+            if let Some(diag) =
                 self.dcx().steal_diagnostic(last_expr_field.span, StashKey::MaybeFruTypo)
             {
                 diag.delay_as_bug();
@@ -2197,7 +2194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut err = self.err_ctxt().type_error_struct_with_diag(
             field.ident.span,
             |actual| match ty.kind() {
-                ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!(
+                ty::Adt(adt, ..) if adt.is_enum() => struct_span_code_err!(
                     self.dcx(),
                     field.ident.span,
                     E0559,
@@ -2207,7 +2204,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     variant.name,
                     field.ident
                 ),
-                _ => struct_span_err!(
+                _ => struct_span_code_err!(
                     self.dcx(),
                     field.ident.span,
                     E0560,
@@ -2835,15 +2832,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn private_field_err(&self, field: Ident, base_did: DefId) -> DiagnosticBuilder<'_> {
         let struct_path = self.tcx().def_path_str(base_did);
         let kind_name = self.tcx().def_descr(base_did);
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             field.span,
             E0616,
             "field `{field}` of {kind_name} `{struct_path}` is private",
-        );
-        err.span_label(field.span, "private field");
-
-        err
+        )
+        .with_span_label(field.span, "private field")
     }
 
     pub(crate) fn get_field_candidates_considering_privacy(
@@ -3163,8 +3158,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         value: &'tcx hir::Expr<'tcx>,
         expr: &'tcx hir::Expr<'tcx>,
     ) -> Ty<'tcx> {
-        match self.resume_yield_tys {
-            Some((resume_ty, yield_ty)) => {
+        match self.coroutine_types {
+            Some(CoroutineTypes { resume_ty, yield_ty }) => {
                 self.check_expr_coercible_to_type(value, yield_ty, None);
 
                 resume_ty
@@ -3184,9 +3179,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized);
 
         if !is_input && !expr.is_syntactic_place_expr() {
-            let mut err = self.dcx().struct_span_err(expr.span, "invalid asm output");
-            err.span_label(expr.span, "cannot assign to this expression");
-            err.emit();
+            self.dcx()
+                .struct_span_err(expr.span, "invalid asm output")
+                .with_span_label(expr.span, "cannot assign to this expression")
+                .emit();
         }
 
         // If this is an input value, we require its type to be fully resolved
@@ -3279,27 +3275,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .iter_enumerated()
                         .find(|(_, v)| v.ident(self.tcx).normalize_to_macros_2_0() == ident)
                     else {
-                        let mut err = type_error_struct!(
+                        type_error_struct!(
                             self.dcx(),
                             ident.span,
                             container,
                             E0599,
                             "no variant named `{ident}` found for enum `{container}`",
-                        );
-                        err.span_label(field.span, "variant not found");
-                        err.emit();
+                        )
+                        .with_span_label(field.span, "variant not found")
+                        .emit();
                         break;
                     };
                     let Some(&subfield) = fields.next() else {
-                        let mut err = type_error_struct!(
+                        type_error_struct!(
                             self.dcx(),
                             ident.span,
                             container,
                             E0795,
                             "`{ident}` is an enum variant; expected field at end of `offset_of`",
-                        );
-                        err.span_label(field.span, "enum variant");
-                        err.emit();
+                        )
+                        .with_span_label(field.span, "enum variant")
+                        .emit();
                         break;
                     };
                     let (subident, sub_def_scope) =
@@ -3310,16 +3306,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         .iter_enumerated()
                         .find(|(_, f)| f.ident(self.tcx).normalize_to_macros_2_0() == subident)
                     else {
-                        let mut err = type_error_struct!(
+                        type_error_struct!(
                             self.dcx(),
                             ident.span,
                             container,
                             E0609,
                             "no field named `{subfield}` on enum variant `{container}::{ident}`",
-                        );
-                        err.span_label(field.span, "this enum variant...");
-                        err.span_label(subident.span, "...does not have this field");
-                        err.emit();
+                        )
+                        .with_span_label(field.span, "this enum variant...")
+                        .with_span_label(subident.span, "...does not have this field")
+                        .emit();
                         break;
                     };
 
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index e952a7ff9e8..ed3dd1e39df 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -669,12 +669,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
         );
         self.walk_pat(discr_place, arm.pat, arm.guard.is_some());
 
-        match arm.guard {
-            Some(hir::Guard::If(e)) => self.consume_expr(e),
-            Some(hir::Guard::IfLet(l)) => {
-                self.walk_local(l.init, l.pat, None, |t| t.borrow_expr(l.init, ty::ImmBorrow))
-            }
-            None => {}
+        if let Some(ref e) = arm.guard {
+            self.consume_expr(e)
         }
 
         self.consume_expr(arm.body);
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 023bd70be17..aa8bbad1d12 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -85,7 +85,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             return false;
         }
 
-        // not setting the `fallback_has_occured` field here because
+        // not setting the `fallback_has_occurred` field here because
         // that field is only used for type fallback diagnostics.
         for effect in unsolved_effects {
             let expected = self.tcx.consts.true_;
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 994f11b57d1..a4cd9ccc984 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -534,7 +534,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let coroutines = std::mem::take(&mut *self.deferred_coroutine_interiors.borrow_mut());
         debug!(?coroutines);
 
-        for &(expr_def_id, body_id, interior, _) in coroutines.iter() {
+        for &(expr_def_id, body_id, interior) in coroutines.iter() {
             debug!(?expr_def_id);
 
             // Create the `CoroutineWitness` type that we will unify with `interior`.
@@ -845,7 +845,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .and_then(|r| {
                 // lint bare trait if the method is found in the trait
                 if span.edition().at_least_rust_2021()
-                    && let Some(mut diag) =
+                    && let Some(diag) =
                         self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
                 {
                     diag.emit();
@@ -877,7 +877,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 // emit or cancel the diagnostic for bare traits
                 if span.edition().at_least_rust_2021()
-                    && let Some(mut diag) =
+                    && let Some(diag) =
                         self.dcx().steal_diagnostic(qself.span, StashKey::TraitMissingMethod)
                 {
                     if trait_missing_method {
@@ -889,7 +889,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if item_name.name != kw::Empty {
-                    if let Some(mut e) = self.report_method_error(
+                    if let Some(e) = self.report_method_error(
                         span,
                         ty.normalized,
                         item_name,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 7b85c492eae..8cd5ed3494b 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -6,7 +6,8 @@ use crate::method::MethodCallee;
 use crate::TupleArgumentsFlag::*;
 use crate::{errors, Expectation::*};
 use crate::{
-    struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy, TupleArgumentsFlag,
+    struct_span_code_err, BreakableCtxt, Diverges, Expectation, FnCtxt, Needs, RawTy,
+    TupleArgumentsFlag,
 };
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxIndexSet;
@@ -204,7 +205,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {
                     // Otherwise, there's a mismatch, so clear out what we're expecting, and set
                     // our input types to err_args so we don't blow up the error messages
-                    struct_span_err!(
+                    struct_span_code_err!(
                         tcx.dcx(),
                         call_span,
                         E0059,
@@ -664,7 +665,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             format!("arguments to this {call_name} are incorrect"),
                         );
                     } else {
-                        err = tcx.dcx().struct_span_err_with_code(
+                        err = tcx.dcx().struct_span_err(
                             full_call_span,
                             format!(
                                 "{call_name} takes {}{} but {} {} supplied",
@@ -676,8 +677,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 potentially_plural_count(provided_args.len(), "argument"),
                                 pluralize!("was", provided_args.len())
                             ),
-                            DiagnosticId::Error(err_code.to_owned()),
                         );
+                        err.code(DiagnosticId::Error(err_code.to_owned()));
                         err.multipart_suggestion_verbose(
                             "wrap these arguments in parentheses to construct a tuple",
                             vec![
@@ -807,7 +808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
-            struct_span_err!(
+            struct_span_code_err!(
                 tcx.dcx(),
                 full_call_span,
                 E0308,
@@ -815,18 +816,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 call_name,
             )
         } else {
-            tcx.dcx().struct_span_err_with_code(
-                full_call_span,
-                format!(
-                    "this {} takes {}{} but {} {} supplied",
-                    call_name,
-                    if c_variadic { "at least " } else { "" },
-                    potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
-                    potentially_plural_count(provided_args.len(), "argument"),
-                    pluralize!("was", provided_args.len())
-                ),
-                DiagnosticId::Error(err_code.to_owned()),
-            )
+            tcx.dcx()
+                .struct_span_err(
+                    full_call_span,
+                    format!(
+                        "this {} takes {}{} but {} {} supplied",
+                        call_name,
+                        if c_variadic { "at least " } else { "" },
+                        potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
+                        potentially_plural_count(provided_args.len(), "argument"),
+                        pluralize!("was", provided_args.len())
+                    ),
+                )
+                .with_code(DiagnosticId::Error(err_code.to_owned()))
         };
 
         // As we encounter issues, keep track of what we want to provide for the suggestion
@@ -1377,14 +1379,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // (issue #88844).
                     guar
                 }
-                _ => struct_span_err!(
+                _ => struct_span_code_err!(
                     self.dcx(),
                     path_span,
                     E0071,
                     "expected struct, variant or union type, found {}",
                     ty.normalized.sort_string(self.tcx)
                 )
-                .span_label(path_span, "not a struct")
+                .with_span_label(path_span, "not a struct")
                 .emit(),
             })
         }
@@ -1459,8 +1461,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             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)
+            if let Some(err) = self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
             {
                 err.emit();
             }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 635284c5f73..fde3d41faec 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -5,7 +5,7 @@ mod checks;
 mod suggestions;
 
 use crate::coercion::DynamicCoerceMany;
-use crate::{Diverges, EnclosingBreakables, Inherited};
+use crate::{CoroutineTypes, Diverges, EnclosingBreakables, Inherited};
 use rustc_errors::{DiagCtxt, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -68,7 +68,7 @@ pub struct FnCtxt<'a, 'tcx> {
     /// First span of a return site that we find. Used in error messages.
     pub(super) ret_coercion_span: Cell<Option<Span>>,
 
-    pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>,
+    pub(super) coroutine_types: Option<CoroutineTypes<'tcx>>,
 
     /// Whether the last checked node generates a divergence (e.g.,
     /// `return` will set this to `Always`). In general, when entering
@@ -122,7 +122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             err_count_on_creation: inh.tcx.dcx().err_count(),
             ret_coercion: None,
             ret_coercion_span: Cell::new(None),
-            resume_yield_tys: None,
+            coroutine_types: None,
             diverges: Cell::new(Diverges::Maybe),
             enclosing_breakables: RefCell::new(EnclosingBreakables {
                 stack: Vec::new(),
diff --git a/compiler/rustc_hir_typeck/src/gather_locals.rs b/compiler/rustc_hir_typeck/src/gather_locals.rs
index e169b45d725..52dc1e85916 100644
--- a/compiler/rustc_hir_typeck/src/gather_locals.rs
+++ b/compiler/rustc_hir_typeck/src/gather_locals.rs
@@ -60,7 +60,7 @@ pub(super) struct GatherLocalsVisitor<'a, 'tcx> {
     // parameters are special cases of patterns, but we want to handle them as
     // *distinct* cases. so track when we are hitting a pattern *within* an fn
     // parameter.
-    outermost_fn_param_pat: Option<Span>,
+    outermost_fn_param_pat: Option<(Span, hir::HirId)>,
 }
 
 impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
@@ -131,7 +131,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
     }
 
     fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
-        let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span);
+        let old_outermost_fn_param_pat =
+            self.outermost_fn_param_pat.replace((param.ty_span, param.hir_id));
         intravisit::walk_param(self, param);
         self.outermost_fn_param_pat = old_outermost_fn_param_pat;
     }
@@ -141,7 +142,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
         if let PatKind::Binding(_, _, ident, _) = p.kind {
             let var_ty = self.assign(p.span, p.hir_id, None);
 
-            if let Some(ty_span) = self.outermost_fn_param_pat {
+            if let Some((ty_span, hir_id)) = self.outermost_fn_param_pat {
                 if !self.fcx.tcx.features().unsized_fn_params {
                     self.fcx.require_type_is_sized(
                         var_ty,
@@ -154,7 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
                             {
                                 None
                             } else {
-                                Some(ty_span)
+                                Some(hir_id)
                             },
                         ),
                     );
diff --git a/compiler/rustc_hir_typeck/src/inherited.rs b/compiler/rustc_hir_typeck/src/inherited.rs
index 7a6a2b2a010..4ad46845f0b 100644
--- a/compiler/rustc_hir_typeck/src/inherited.rs
+++ b/compiler/rustc_hir_typeck/src/inherited.rs
@@ -55,8 +55,7 @@ pub struct Inherited<'tcx> {
 
     pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>,
 
-    pub(super) deferred_coroutine_interiors:
-        RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>, hir::CoroutineKind)>>,
+    pub(super) deferred_coroutine_interiors: RefCell<Vec<(LocalDefId, hir::BodyId, Ty<'tcx>)>>,
 
     /// Whenever we introduce an adjustment from `!` into a type variable,
     /// we record that type variable here. This is later used to inform
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 44d28123fa3..e087733130e 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -1,5 +1,5 @@
 use hir::HirId;
-use rustc_errors::struct_span_err;
+use rustc_errors::struct_span_code_err;
 use rustc_hir as hir;
 use rustc_index::Idx;
 use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
@@ -73,10 +73,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to)
                 && size_to == Pointer(dl.instruction_address_space).size(&tcx)
             {
-                struct_span_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
-                    .note(format!("source type: {from}"))
-                    .note(format!("target type: {to}"))
-                    .help("cast with `as` to a pointer instead")
+                struct_span_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
+                    .with_note(format!("source type: {from}"))
+                    .with_note(format!("target type: {to}"))
+                    .with_help("cast with `as` to a pointer instead")
                     .emit();
                 return;
             }
@@ -112,7 +112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(err) => err.to_string(),
         };
 
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             tcx.dcx(),
             span,
             E0512,
@@ -121,6 +121,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
         if from == to {
             err.note(format!("`{from}` does not have a fixed size"));
+            err.emit();
         } else {
             err.note(format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
                 .note(format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
@@ -128,8 +129,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 err.delay_as_bug();
             } else if let Err(LayoutError::ReferencesError(_)) = sk_to {
                 err.delay_as_bug();
+            } else {
+                err.emit();
             }
         }
-        err.emit();
     }
 }
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index da9a2bde783..36dd06d944c 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -52,7 +52,7 @@ use crate::expectation::Expectation;
 use crate::fn_ctxt::RawTy;
 use crate::gather_locals::GatherLocalsVisitor;
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{struct_span_err, DiagnosticId, ErrorGuaranteed};
+use rustc_errors::{struct_span_code_err, DiagnosticId, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::Visitor;
@@ -72,7 +72,7 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 #[macro_export]
 macro_rules! type_error_struct {
     ($dcx:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
-        let mut err = rustc_errors::struct_span_err!($dcx, $span, $code, $($message)*);
+        let mut err = rustc_errors::struct_span_code_err!($dcx, $span, $code, $($message)*);
 
         if $typ.references_error() {
             err.downgrade_to_delayed_bug();
@@ -181,7 +181,7 @@ fn typeck_with_fallback<'tcx>(
     let mut fcx = FnCtxt::new(&inh, param_env, def_id);
 
     if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
-        let fn_sig = if rustc_hir_analysis::collect::get_infer_ret_ty(&decl.output).is_some() {
+        let fn_sig = if decl.output.get_infer_ret_ty().is_some() {
             fcx.astconv().ty_of_fn(id, header.unsafety, header.abi, decl, None, None)
         } else {
             tcx.fn_sig(def_id).instantiate_identity()
@@ -193,7 +193,7 @@ fn typeck_with_fallback<'tcx>(
         let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
         let fn_sig = fcx.normalize(body.value.span, fn_sig);
 
-        check_fn(&mut fcx, fn_sig, decl, def_id, body, None, tcx.features().unsized_fn_params);
+        check_fn(&mut fcx, fn_sig, None, decl, def_id, body, tcx.features().unsized_fn_params);
     } else {
         let expected_type = if let Some(&hir::Ty { kind: hir::TyKind::Infer, span, .. }) = body_ty {
             Some(fcx.next_ty_var(TypeVariableOrigin {
@@ -295,15 +295,13 @@ fn typeck_with_fallback<'tcx>(
 /// When `check_fn` is invoked on a coroutine (i.e., a body that
 /// includes yield), it returns back some information about the yield
 /// points.
+#[derive(Debug, PartialEq, Copy, Clone)]
 struct CoroutineTypes<'tcx> {
     /// Type of coroutine argument / values returned by `yield`.
     resume_ty: Ty<'tcx>,
 
     /// Type of value that is yielded.
     yield_ty: Ty<'tcx>,
-
-    /// Types that are captured (see `CoroutineInterior` for more).
-    interior: Ty<'tcx>,
 }
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -368,18 +366,17 @@ fn report_unexpected_variant_res(
         _ => res.descr(),
     };
     let path_str = rustc_hir_pretty::qpath_to_string(qpath);
-    let mut err = tcx.dcx().struct_span_err_with_code(
-        span,
-        format!("expected {expected}, found {res_descr} `{path_str}`"),
-        DiagnosticId::Error(err_code.into()),
-    );
+    let err = tcx
+        .dcx()
+        .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`"))
+        .with_code(DiagnosticId::Error(err_code.into()));
     match res {
         Res::Def(DefKind::Fn | DefKind::AssocFn, _) if err_code == "E0164" => {
             let patterns_url = "https://doc.rust-lang.org/book/ch18-00-patterns.html";
-            err.span_label(span, "`fn` calls are not allowed in patterns");
-            err.help(format!("for more information, visit {patterns_url}"))
+            err.with_span_label(span, "`fn` calls are not allowed in patterns")
+                .with_help(format!("for more information, visit {patterns_url}"))
         }
-        _ => err.span_label(span, format!("not a {expected}")),
+        _ => err.with_span_label(span, format!("not a {expected}")),
     }
     .emit()
 }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 47fdd64796e..1f01c6b7406 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::StashKey;
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
+    pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -148,7 +148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             MethodError::Ambiguity(mut sources) => {
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     self.dcx(),
                     item_name.span,
                     E0034,
@@ -171,7 +171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => {
                 let kind = self.tcx.def_kind_descr(kind, def_id);
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     self.dcx(),
                     item_name.span,
                     E0624,
@@ -263,8 +263,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> DiagnosticBuilder<'_> {
         let mut file = None;
         let ty_str = self.tcx.short_ty_string(rcvr_ty, &mut file);
-        let mut err =
-            struct_span_err!(self.dcx(), rcvr_expr.span, E0599, "cannot write into `{}`", ty_str);
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            rcvr_expr.span,
+            E0599,
+            "cannot write into `{}`",
+            ty_str
+        );
         err.span_note(
             rcvr_expr.span,
             "must implement `io::Write`, `fmt::Write`, or have a `write_fmt` method",
@@ -913,7 +918,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             for ((span, add_where_or_comma), obligations) in type_params.into_iter() {
                 restrict_type_params = true;
                 // #74886: Sort here so that the output is always the same.
-                let obligations = obligations.to_sorted_stable_ord();
+                let obligations = obligations.into_sorted_stable_ord();
                 err.span_suggestion_verbose(
                     span,
                     format!(
@@ -961,7 +966,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                          but its trait bounds were not satisfied"
                     )
                 });
-                err.set_primary_message(primary_message);
+                err.primary_message(primary_message);
                 if let Some(label) = label {
                     custom_span_label = true;
                     err.span_label(span, label);
@@ -1836,7 +1841,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && !actual.has_concrete_skeleton()
             && let SelfSource::MethodCall(expr) = source
         {
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 tcx.dcx(),
                 span,
                 E0689,
@@ -2252,6 +2257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         err: &mut Diagnostic,
         errors: Vec<FulfillmentError<'tcx>>,
+        suggest_derive: bool,
     ) {
         let all_local_types_needing_impls =
             errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
@@ -2322,10 +2328,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .iter()
             .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone())))
             .collect();
-        self.suggest_derive(err, &preds);
+        if suggest_derive {
+            self.suggest_derive(err, &preds);
+        } else {
+            // The predicate comes from a binop where the lhs and rhs have different types.
+            let _ = self.note_predicate_source_and_get_derives(err, &preds);
+        }
     }
 
-    pub fn suggest_derive(
+    fn note_predicate_source_and_get_derives(
         &self,
         err: &mut Diagnostic,
         unsatisfied_predicates: &[(
@@ -2333,7 +2344,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Option<ty::Predicate<'tcx>>,
             Option<ObligationCause<'tcx>>,
         )],
-    ) {
+    ) -> Vec<(String, Span, Symbol)> {
         let mut derives = Vec::<(String, Span, Symbol)>::new();
         let mut traits = Vec::new();
         for (pred, _, _) in unsatisfied_predicates {
@@ -2382,21 +2393,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         traits.sort();
         traits.dedup();
 
-        derives.sort();
-        derives.dedup();
-
-        let mut derives_grouped = Vec::<(String, Span, String)>::new();
-        for (self_name, self_span, trait_name) in derives.into_iter() {
-            if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut()
-            {
-                if last_self_name == &self_name {
-                    last_trait_names.push_str(format!(", {trait_name}").as_str());
-                    continue;
-                }
-            }
-            derives_grouped.push((self_name, self_span, trait_name.to_string()));
-        }
-
         let len = traits.len();
         if len > 0 {
             let span =
@@ -2419,6 +2415,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             );
         }
 
+        derives
+    }
+
+    pub(crate) fn suggest_derive(
+        &self,
+        err: &mut Diagnostic,
+        unsatisfied_predicates: &[(
+            ty::Predicate<'tcx>,
+            Option<ty::Predicate<'tcx>>,
+            Option<ObligationCause<'tcx>>,
+        )],
+    ) {
+        let mut derives = self.note_predicate_source_and_get_derives(err, unsatisfied_predicates);
+        derives.sort();
+        derives.dedup();
+
+        let mut derives_grouped = Vec::<(String, Span, String)>::new();
+        for (self_name, self_span, trait_name) in derives.into_iter() {
+            if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut()
+            {
+                if last_self_name == &self_name {
+                    last_trait_names.push_str(format!(", {trait_name}").as_str());
+                    continue;
+                }
+            }
+            derives_grouped.push((self_name, self_span, trait_name.to_string()));
+        }
+
         for (self_name, self_span, traits) in &derives_grouped {
             err.span_suggestion_verbose(
                 self_span.shrink_to_lo(),
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index de3d5f498d5..ee411f8ed5f 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -4,7 +4,7 @@ use super::method::MethodCallee;
 use super::{has_expected_num_generic_args, FnCtxt};
 use crate::Expectation;
 use rustc_ast as ast;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::ObligationCauseCode;
@@ -306,7 +306,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
                 let (mut err, output_def_id) = match is_assign {
                     IsAssign::Yes => {
-                        let mut err = struct_span_err!(
+                        let mut err = struct_span_code_err!(
                             self.dcx(),
                             expr.span,
                             E0368,
@@ -318,7 +318,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             lhs_expr.span,
                             format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty),
                         );
-                        self.note_unmet_impls_on_type(&mut err, errors);
+                        self.note_unmet_impls_on_type(&mut err, errors, false);
                         (err, None)
                     }
                     IsAssign::No => {
@@ -370,12 +370,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                 })
                                 .cloned()
                         });
-                        let mut err = struct_span_err!(self.dcx(), op.span, E0369, "{message}");
+                        let mut err =
+                            struct_span_code_err!(self.dcx(), op.span, E0369, "{message}");
                         if !lhs_expr.span.eq(&rhs_expr.span) {
                             err.span_label(lhs_expr.span, lhs_ty.to_string());
                             err.span_label(rhs_expr.span, rhs_ty.to_string());
                         }
-                        self.note_unmet_impls_on_type(&mut err, errors);
+                        let suggest_derive = self.can_eq(self.param_env, lhs_ty, rhs_ty);
+                        self.note_unmet_impls_on_type(&mut err, errors, suggest_derive);
                         (err, output_def_id)
                     }
                 };
@@ -384,7 +386,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && let hir::ExprKind::Assign(..) = expr.kind
                 {
                     // We defer to the later error produced by `check_lhs_assignable`.
-                    err.delay_as_bug();
+                    err.downgrade_to_delayed_bug();
                 }
 
                 let suggest_deref_binop =
@@ -787,7 +789,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             Err(errors) => {
                 let actual = self.resolve_vars_if_possible(operand_ty);
                 let guar = actual.error_reported().err().unwrap_or_else(|| {
-                    let mut err = struct_span_err!(
+                    let mut err = struct_span_code_err!(
                         self.dcx(),
                         ex.span,
                         E0600,
@@ -852,7 +854,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             Str | Never | Char | Tuple(_) | Array(_, _) => {}
                             Ref(_, lty, _) if *lty.kind() == Str => {}
                             _ => {
-                                self.note_unmet_impls_on_type(&mut err, errors);
+                                self.note_unmet_impls_on_type(&mut err, errors, true);
                             }
                         }
                     }
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 02a35110716..95813cb68a6 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -3,7 +3,7 @@ use crate::{errors, FnCtxt, RawTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     MultiSpan,
 };
 use rustc_hir as hir;
@@ -117,7 +117,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         actual: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) {
-        if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
+        if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
             err.emit();
         }
     }
@@ -441,7 +441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         //
         // then that's equivalent to there existing a LUB.
         let cause = self.pattern_cause(ti, span);
-        if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
+        if let Some(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) {
             err.emit_unless(
                 ti.span
                     .filter(|&s| {
@@ -546,7 +546,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             (_, Some((true, _, sp))) => sp,
             _ => span_bug!(span, "emit_err_pat_range: no side failed or exists but still error?"),
         };
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0029,
@@ -837,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // This is "x = SomeTrait" being reduced from
             // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error.
             let type_str = self.ty_to_string(expected);
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 self.dcx(),
                 span,
                 E0033,
@@ -1103,7 +1103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Type-check the tuple struct pattern against the expected type.
         let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti);
-        let had_err = if let Some(mut err) = diag {
+        let had_err = if let Some(err) = diag {
             err.emit();
             true
         } else {
@@ -1171,7 +1171,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let last_field_def_span = *field_def_spans.last().unwrap();
 
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             MultiSpan::from_spans(subpat_spans),
             E0023,
@@ -1329,9 +1329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         });
         let element_tys = tcx.mk_type_list_from_iter(element_tys_iter);
         let pat_ty = Ty::new_tup(tcx, element_tys);
-        if let Some(mut err) =
-            self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info)
-        {
+        if let Some(err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, pat_info.top_info) {
             let reported = err.emit();
             // Walk subpatterns with an expected type of `err` in this case to silence
             // further errors being emitted when using the bindings. #50333
@@ -1469,8 +1467,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
         match (inexistent_fields_err, unmentioned_err) {
-            (Some(mut i), Some(mut u)) => {
-                if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+            (Some(i), Some(u)) => {
+                if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
                     // We don't want to show the nonexistent fields error when this was
                     // `Foo { a, b }` when it should have been `Foo(a, b)`.
                     i.delay_as_bug();
@@ -1481,19 +1479,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     u.emit();
                 }
             }
-            (None, Some(mut u)) => {
-                if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
+            (None, Some(u)) => {
+                if let Some(e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) {
                     u.delay_as_bug();
                     e.emit();
                 } else {
                     u.emit();
                 }
             }
-            (Some(mut err), None) => {
+            (Some(err), None) => {
                 err.emit();
             }
             (None, None)
-                if let Some(mut err) =
+                if let Some(err) =
                     self.error_tuple_variant_index_shorthand(variant, pat, fields) =>
             {
                 err.emit();
@@ -1518,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand);
             if has_shorthand_field_name {
                 let path = rustc_hir_pretty::qpath_to_string(qpath);
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     self.dcx(),
                     pat.span,
                     E0769,
@@ -1543,19 +1541,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi()));
         let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" };
 
-        let mut err = struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             pat.span,
             E0638,
             "`..` required with {descr} marked as non-exhaustive",
-        );
-        err.span_suggestion_verbose(
+        )
+        .with_span_suggestion_verbose(
             sp_comma,
             "add `..` at the end of the field list to ignore all other fields",
             sugg,
             Applicability::MachineApplicable,
-        );
-        err.emit();
+        )
+        .emit();
     }
 
     fn error_field_already_bound(
@@ -1564,15 +1562,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ident: Ident,
         other_field: Span,
     ) -> ErrorGuaranteed {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0025,
             "field `{}` bound multiple times in the pattern",
             ident
         )
-        .span_label(span, format!("multiple uses of `{ident}` in pattern"))
-        .span_label(other_field, format!("first use of `{ident}`"))
+        .with_span_label(span, format!("multiple uses of `{ident}` in pattern"))
+        .with_span_label(other_field, format!("first use of `{ident}`"))
         .emit()
     }
 
@@ -1603,7 +1601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             )
         };
         let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::<Vec<_>>();
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             tcx.dcx(),
             spans,
             E0026,
@@ -1700,7 +1698,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             let path = rustc_hir_pretty::qpath_to_string(qpath);
-            let mut err = struct_span_err!(
+            let mut err = struct_span_code_err!(
                 self.dcx(),
                 pat.span,
                 E0769,
@@ -1878,7 +1876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .join(", ");
             format!("fields {fields}{inaccessible}")
         };
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             pat.span,
             E0027,
@@ -2228,7 +2226,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         min_len: u64,
         size: u64,
     ) -> ErrorGuaranteed {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0527,
@@ -2237,7 +2235,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             pluralize!(min_len),
             size,
         )
-        .span_label(span, format!("expected {} element{}", size, pluralize!(size)))
+        .with_span_label(span, format!("expected {} element{}", size, pluralize!(size)))
         .emit()
     }
 
@@ -2247,7 +2245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         min_len: u64,
         size: u64,
     ) -> ErrorGuaranteed {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0528,
@@ -2256,7 +2254,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             pluralize!(min_len),
             size,
         )
-        .span_label(
+        .with_span_label(
             span,
             format!("pattern cannot match array of {} element{}", size, pluralize!(size),),
         )
@@ -2264,7 +2262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     fn error_scrutinee_unfixed_length(&self, span: Span) -> ErrorGuaranteed {
-        struct_span_err!(
+        struct_span_code_err!(
             self.dcx(),
             span,
             E0730,
@@ -2279,7 +2277,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         ti: TopInfo<'tcx>,
     ) -> ErrorGuaranteed {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0529,
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index c3bcdcfa5cd..d6261763772 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -332,15 +332,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if inside_union
                             && source.ty_adt_def().is_some_and(|adt| adt.is_manually_drop())
                         {
-                            let mut err = self.dcx().struct_span_err(
+                            self.dcx().struct_span_err(
                                 expr.span,
                                 "not automatically applying `DerefMut` on `ManuallyDrop` union field",
-                            );
-                            err.help(
+                            )
+                            .with_help(
                                 "writing to this reference calls the destructor for the old value",
-                            );
-                            err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor");
-                            err.emit();
+                            )
+                            .with_help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor")
+                            .emit();
                         }
                     }
                     source = adjustment.target;
diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs
index 47b9d5f6503..f6b05e1b35a 100644
--- a/compiler/rustc_hir_typeck/src/upvar.rs
+++ b/compiler/rustc_hir_typeck/src/upvar.rs
@@ -912,7 +912,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         drop_order: bool,
     ) -> MigrationWarningReason {
         MigrationWarningReason {
-            auto_traits: auto_trait_reasons.to_sorted_stable_ord(),
+            auto_traits: auto_trait_reasons.into_sorted_stable_ord(),
             drop_order,
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 719d85ed3db..c56a028321a 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -473,7 +473,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
         assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
 
         let fcx_coercion_casts = fcx_typeck_results.coercion_casts().to_sorted_stable_ord();
-        for local_id in fcx_coercion_casts {
+        for &local_id in fcx_coercion_casts {
             self.typeck_results.set_coercion_cast(local_id);
         }
     }
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 2c6ae91786d..e18b1365d9c 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -273,7 +273,7 @@ pub(crate) fn prepare_session_directory(
             debug!("successfully copied data from: {}", source_directory.display());
 
             if !allows_links {
-                sess.dcx().emit_warning(errors::HardLinkFailed { path: &session_dir });
+                sess.dcx().emit_warn(errors::HardLinkFailed { path: &session_dir });
             }
 
             sess.init_incr_comp_session(session_dir, directory_lock);
@@ -288,7 +288,7 @@ pub(crate) fn prepare_session_directory(
             // Try to remove the session directory we just allocated. We don't
             // know if there's any garbage in it from the failed copy action.
             if let Err(err) = safe_remove_dir_all(&session_dir) {
-                sess.dcx().emit_warning(errors::DeletePartial { path: &session_dir, err });
+                sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err });
             }
 
             delete_session_dir_lock_file(sess, &lock_file_path);
@@ -322,7 +322,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
         );
 
         if let Err(err) = safe_remove_dir_all(&*incr_comp_session_dir) {
-            sess.dcx().emit_warning(errors::DeleteFull { path: &incr_comp_session_dir, err });
+            sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err });
         }
 
         let lock_file_path = lock_file_path(&*incr_comp_session_dir);
@@ -365,7 +365,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
         }
         Err(e) => {
             // Warn about the error. However, no need to abort compilation now.
-            sess.dcx().emit_warning(errors::Finalize { path: &incr_comp_session_dir, err: e });
+            sess.dcx().emit_warn(errors::Finalize { path: &incr_comp_session_dir, err: e });
 
             debug!("finalize_session_directory() - error, marking as invalid");
             // Drop the file lock, so we can garage collect
@@ -500,7 +500,7 @@ fn lock_directory(
 
 fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) {
     if let Err(err) = safe_remove_file(lock_file_path) {
-        sess.dcx().emit_warning(errors::DeleteLock { path: lock_file_path, err });
+        sess.dcx().emit_warn(errors::DeleteLock { path: lock_file_path, err });
     }
 }
 
@@ -724,7 +724,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
         if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) {
             let path = crate_directory.join(directory_name);
             if let Err(err) = safe_remove_dir_all(&path) {
-                sess.dcx().emit_warning(errors::InvalidGcFailed { path: &path, err });
+                sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err });
             }
         }
     }
@@ -830,7 +830,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
         debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
 
         if let Err(err) = safe_remove_dir_all(&path) {
-            sess.dcx().emit_warning(errors::FinalizedGcFailed { path: &path, err });
+            sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err });
         } else {
             delete_session_dir_lock_file(sess, &lock_file_path(&path));
         }
@@ -848,7 +848,7 @@ fn delete_old(sess: &Session, path: &Path) {
     debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
 
     if let Err(err) = safe_remove_dir_all(path) {
-        sess.dcx().emit_warning(errors::SessionGcFailed { path: path, err });
+        sess.dcx().emit_warn(errors::SessionGcFailed { path: path, err });
     } else {
         delete_session_dir_lock_file(sess, &lock_file_path(path));
     }
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index ce8f5bb69ae..96bfe766c20 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -51,7 +51,7 @@ impl<T: Default> LoadResult<T> {
 
         match self {
             LoadResult::LoadDepGraph(path, err) => {
-                sess.dcx().emit_warning(errors::LoadDepGraph { path, err });
+                sess.dcx().emit_warn(errors::LoadDepGraph { path, err });
                 Default::default()
             }
             LoadResult::DataOutOfDate => {
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index bdc935a5e3b..08b7d08bcc0 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -103,7 +103,7 @@ pub fn save_work_product_index(
     // deleted during invalidation. Some object files don't change their
     // content, they are just not needed anymore.
     let previous_work_products = dep_graph.previous_work_products();
-    for (id, wp) in previous_work_products.to_sorted_stable_ord().iter() {
+    for (id, wp) in previous_work_products.to_sorted_stable_ord() {
         if !new_work_products.contains_key(id) {
             work_product::delete_workproduct_files(sess, wp);
             debug_assert!(
diff --git a/compiler/rustc_incremental/src/persist/work_product.rs b/compiler/rustc_incremental/src/persist/work_product.rs
index 1450d8a99ab..906233ef53e 100644
--- a/compiler/rustc_incremental/src/persist/work_product.rs
+++ b/compiler/rustc_incremental/src/persist/work_product.rs
@@ -30,7 +30,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir(
                 let _ = saved_files.insert(ext.to_string(), file_name);
             }
             Err(err) => {
-                sess.dcx().emit_warning(errors::CopyWorkProductToCache {
+                sess.dcx().emit_warn(errors::CopyWorkProductToCache {
                     from: path,
                     to: &path_in_incr_dir,
                     err,
@@ -50,7 +50,7 @@ pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduc
     for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() {
         let path = in_incr_comp_dir_sess(sess, path);
         if let Err(err) = std_fs::remove_file(&path) {
-            sess.dcx().emit_warning(errors::DeleteWorkProduct { path: &path, err });
+            sess.dcx().emit_warn(errors::DeleteWorkProduct { path: &path, err });
         }
     }
 }
diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs
index d730ef58deb..dfa3ced9dc1 100644
--- a/compiler/rustc_index/src/bit_set.rs
+++ b/compiler/rustc_index/src/bit_set.rs
@@ -10,7 +10,7 @@ use arrayvec::ArrayVec;
 use smallvec::{smallvec, SmallVec};
 
 #[cfg(feature = "nightly")]
-use rustc_macros::{Decodable, Encodable};
+use rustc_macros::{Decodable_Generic, Encodable_Generic};
 
 use crate::{Idx, IndexVec};
 
@@ -112,7 +112,7 @@ macro_rules! bit_relations_inherent_impls {
 /// to or greater than the domain size. All operations that involve two bitsets
 /// will panic if the bitsets have differing domain sizes.
 ///
-#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
+#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))]
 #[derive(Eq, PartialEq, Hash)]
 pub struct BitSet<T> {
     domain_size: usize,
@@ -1590,7 +1590,7 @@ impl<T: Idx> From<BitSet<T>> for GrowableBitSet<T> {
 ///
 /// All operations that involve a row and/or column index will panic if the
 /// index exceeds the relevant bound.
-#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
+#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))]
 #[derive(Clone, Eq, PartialEq, Hash)]
 pub struct BitMatrix<R: Idx, C: Idx> {
     num_rows: usize,
@@ -1699,14 +1699,15 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
         let (read_start, read_end) = self.range(read);
         let (write_start, write_end) = self.range(write);
         let words = &mut self.words[..];
-        let mut changed = false;
+        let mut changed = 0;
         for (read_index, write_index) in iter::zip(read_start..read_end, write_start..write_end) {
             let word = words[write_index];
             let new_word = word | words[read_index];
             words[write_index] = new_word;
-            changed |= word != new_word;
+            // See `bitwise` for the rationale.
+            changed |= word ^ new_word;
         }
-        changed
+        changed != 0
     }
 
     /// Adds the bits from `with` to the bits from row `write`, and
@@ -1715,14 +1716,7 @@ impl<R: Idx, C: Idx> BitMatrix<R, C> {
         assert!(write.index() < self.num_rows);
         assert_eq!(with.domain_size(), self.num_columns);
         let (write_start, write_end) = self.range(write);
-        let mut changed = false;
-        for (read_index, write_index) in iter::zip(0..with.words.len(), write_start..write_end) {
-            let word = self.words[write_index];
-            let new_word = word | with.words[read_index];
-            self.words[write_index] = new_word;
-            changed |= word != new_word;
-        }
-        changed
+        bitwise(&mut self.words[write_start..write_end], &with.words, |a, b| a | b)
     }
 
     /// Sets every cell in `row` to true.
@@ -2020,7 +2014,7 @@ impl std::fmt::Debug for FiniteBitSet<u32> {
 
 /// A fixed-sized bitset type represented by an integer type. Indices outwith than the range
 /// representable by `T` are considered set.
-#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
+#[cfg_attr(feature = "nightly", derive(Decodable_Generic, Encodable_Generic))]
 #[derive(Copy, Clone, Eq, PartialEq)]
 pub struct FiniteBitSet<T: FiniteBitSetTy>(pub T);
 
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index a0768fc7115..aee99063e03 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -247,8 +247,8 @@ impl AddToDiagnostic for RegionOriginNote<'_> {
             }
             RegionOriginNote::WithName { span, msg, name, continues } => {
                 label_or_note(span, msg);
-                diag.set_arg("name", name);
-                diag.set_arg("continues", continues);
+                diag.arg("name", name);
+                diag.arg("continues", continues);
             }
             RegionOriginNote::WithRequirement {
                 span,
@@ -256,7 +256,7 @@ impl AddToDiagnostic for RegionOriginNote<'_> {
                 expected_found: Some((expected, found)),
             } => {
                 label_or_note(span, fluent::infer_subtype);
-                diag.set_arg("requirement", requirement);
+                diag.arg("requirement", requirement);
 
                 diag.note_expected_found(&"", expected, &"", found);
             }
@@ -265,7 +265,7 @@ impl AddToDiagnostic for RegionOriginNote<'_> {
                 // handling of region checking when type errors are present is
                 // *terrible*.
                 label_or_note(span, fluent::infer_subtype_2);
-                diag.set_arg("requirement", requirement);
+                diag.arg("requirement", requirement);
             }
         };
     }
@@ -298,8 +298,8 @@ impl AddToDiagnostic for LifetimeMismatchLabels {
                 diag.span_label(param_span, fluent::infer_declared_different);
                 diag.span_label(ret_span, fluent::infer_nothing);
                 diag.span_label(span, fluent::infer_data_returned);
-                diag.set_arg("label_var1_exists", label_var1.is_some());
-                diag.set_arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
+                diag.arg("label_var1_exists", label_var1.is_some());
+                diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
             }
             LifetimeMismatchLabels::Normal {
                 hir_equal,
@@ -317,16 +317,10 @@ impl AddToDiagnostic for LifetimeMismatchLabels {
                     diag.span_label(ty_sup, fluent::infer_types_declared_different);
                     diag.span_label(ty_sub, fluent::infer_nothing);
                     diag.span_label(span, fluent::infer_data_flows);
-                    diag.set_arg("label_var1_exists", label_var1.is_some());
-                    diag.set_arg(
-                        "label_var1",
-                        label_var1.map(|x| x.to_string()).unwrap_or_default(),
-                    );
-                    diag.set_arg("label_var2_exists", label_var2.is_some());
-                    diag.set_arg(
-                        "label_var2",
-                        label_var2.map(|x| x.to_string()).unwrap_or_default(),
-                    );
+                    diag.arg("label_var1_exists", label_var1.is_some());
+                    diag.arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default());
+                    diag.arg("label_var2_exists", label_var2.is_some());
+                    diag.arg("label_var2", label_var2.map(|x| x.to_string()).unwrap_or_default());
                 }
             }
         }
@@ -417,7 +411,7 @@ impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> {
                 suggestions,
                 Applicability::MaybeIncorrect,
             );
-            diag.set_arg("is_impl", is_impl);
+            diag.arg("is_impl", is_impl);
             true
         };
         if mk_suggestion() && self.add_note {
@@ -878,8 +872,8 @@ impl AddToDiagnostic for MoreTargeted {
         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         diag.code(rustc_errors::error_code!(E0772));
-        diag.set_primary_message(fluent::infer_more_targeted);
-        diag.set_arg("ident", self.ident);
+        diag.primary_message(fluent::infer_more_targeted);
+        diag.arg("ident", self.ident);
     }
 }
 
@@ -1299,7 +1293,7 @@ impl AddToDiagnostic for SuggestTuplePatternMany {
     where
         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
-        diag.set_arg("path", self.path);
+        diag.arg("path", self.path);
         let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into());
         diag.multipart_suggestions(
             message,
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
index 68bf36a1615..8e45cc6d80e 100644
--- a/compiler/rustc_infer/src/errors/note_and_explain.rs
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -164,10 +164,10 @@ impl AddToDiagnostic for RegionExplanation<'_> {
     where
         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
-        diag.set_arg("pref_kind", self.prefix);
-        diag.set_arg("suff_kind", self.suffix);
-        diag.set_arg("desc_kind", self.desc.kind);
-        diag.set_arg("desc_arg", self.desc.arg);
+        diag.arg("pref_kind", self.prefix);
+        diag.arg("suff_kind", self.suffix);
+        diag.arg("desc_kind", self.desc.kind);
+        diag.arg("desc_arg", self.desc.arg);
 
         let msg = f(diag, fluent::infer_region_explanation.into());
         if let Some(span) = self.desc.span {
diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
index 69a96448467..e4b37f05b77 100644
--- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
+++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs
@@ -41,7 +41,33 @@ impl<'tcx> InferCtxt<'tcx> {
     where
         V: TypeFoldable<TyCtxt<'tcx>>,
     {
-        self.canonicalize_query_with_mode(value, query_state, &CanonicalizeAllFreeRegions)
+        let (param_env, value) = value.into_parts();
+        let param_env = self.tcx.canonical_param_env_cache.get_or_insert(
+            self.tcx,
+            param_env,
+            query_state,
+            |tcx, param_env, query_state| {
+                // FIXME(#118965): We don't canonicalize the static lifetimes that appear in the
+                // `param_env` beacause they are treated differently by trait selection.
+                Canonicalizer::canonicalize(
+                    param_env,
+                    None,
+                    tcx,
+                    &CanonicalizeFreeRegionsOtherThanStatic,
+                    query_state,
+                )
+            },
+        );
+
+        Canonicalizer::canonicalize_with_base(
+            param_env,
+            value,
+            Some(self),
+            self.tcx,
+            &CanonicalizeAllFreeRegions,
+            query_state,
+        )
+        .unchecked_map(|(param_env, value)| param_env.and(value))
     }
 
     /// Canonicalizes a query *response* `V`. When we canonicalize a
@@ -96,61 +122,6 @@ impl<'tcx> InferCtxt<'tcx> {
             &mut query_state,
         )
     }
-
-    /// A variant of `canonicalize_query` that does not
-    /// canonicalize `'static`. This is useful when
-    /// the query implementation can perform more efficient
-    /// handling of `'static` regions (e.g. trait evaluation).
-    pub fn canonicalize_query_keep_static<V>(
-        &self,
-        value: ty::ParamEnvAnd<'tcx, V>,
-        query_state: &mut OriginalQueryValues<'tcx>,
-    ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>>
-    where
-        V: TypeFoldable<TyCtxt<'tcx>>,
-    {
-        self.canonicalize_query_with_mode(
-            value,
-            query_state,
-            &CanonicalizeFreeRegionsOtherThanStatic,
-        )
-    }
-
-    fn canonicalize_query_with_mode<V>(
-        &self,
-        value: ty::ParamEnvAnd<'tcx, V>,
-        query_state: &mut OriginalQueryValues<'tcx>,
-        canonicalize_region_mode: &dyn CanonicalizeMode,
-    ) -> Canonical<'tcx, ty::ParamEnvAnd<'tcx, V>>
-    where
-        V: TypeFoldable<TyCtxt<'tcx>>,
-    {
-        let (param_env, value) = value.into_parts();
-        let base = self.tcx.canonical_param_env_cache.get_or_insert(
-            self.tcx,
-            param_env,
-            query_state,
-            |tcx, param_env, query_state| {
-                Canonicalizer::canonicalize(
-                    param_env,
-                    None,
-                    tcx,
-                    &CanonicalizeFreeRegionsOtherThanStatic,
-                    query_state,
-                )
-            },
-        );
-
-        Canonicalizer::canonicalize_with_base(
-            base,
-            value,
-            Some(self),
-            self.tcx,
-            canonicalize_region_mode,
-            query_state,
-        )
-        .unchecked_map(|(param_env, value)| param_env.and(value))
-    }
 }
 
 /// Controls how we canonicalize "free regions" that are not inference
@@ -221,10 +192,10 @@ impl CanonicalizeMode for CanonicalizeQueryResponse {
                 // rust-lang/rust#57464: `impl Trait` can leak local
                 // scopes (in manner violating typeck). Therefore, use
                 // `span_delayed_bug` to allow type error over an ICE.
-                canonicalizer.tcx.dcx().span_delayed_bug(
-                    rustc_span::DUMMY_SP,
-                    format!("unexpected region in query response: `{r:?}`"),
-                );
+                canonicalizer
+                    .tcx
+                    .dcx()
+                    .delayed_bug(format!("unexpected region in query response: `{r:?}`"));
                 r
             }
         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index b5a6374ec4b..875e94fcd9f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -60,8 +60,8 @@ use crate::traits::{
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
-    error_code, pluralize, struct_span_err, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg,
+    error_code, pluralize, struct_span_code_err, Applicability, DiagCtxt, Diagnostic,
+    DiagnosticBuilder, DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -361,7 +361,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
             );
         }
         ty::ReError(_) => {
-            err.delay_as_bug();
+            err.downgrade_to_delayed_bug();
         }
         _ => {
             // Ugh. This is a painful case: the hidden region is not one
@@ -2348,23 +2348,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             GenericKind::Param(ref p) => format!("the parameter type `{p}`"),
             GenericKind::Placeholder(ref p) => format!("the placeholder type `{p:?}`"),
             GenericKind::Alias(ref p) => match p.kind(self.tcx) {
-                ty::AliasKind::Projection | ty::AliasKind::Inherent => {
+                ty::Projection | ty::Inherent => {
                     format!("the associated type `{p}`")
                 }
-                ty::AliasKind::Weak => format!("the type alias `{p}`"),
-                ty::AliasKind::Opaque => format!("the opaque type `{p}`"),
+                ty::Weak => format!("the type alias `{p}`"),
+                ty::Opaque => format!("the opaque type `{p}`"),
             },
         };
 
-        let mut err = self.tcx.dcx().struct_span_err_with_code(
-            span,
-            format!("{labeled_user_string} may not live long enough"),
-            match sub.kind() {
-                ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => error_code!(E0309),
-                ty::ReStatic => error_code!(E0310),
-                _ => error_code!(E0311),
-            },
-        );
+        let mut err = self
+            .tcx
+            .dcx()
+            .struct_span_err(span, format!("{labeled_user_string} may not live long enough"));
+        err.code(match sub.kind() {
+            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => error_code!(E0309),
+            ty::ReStatic => error_code!(E0310),
+            _ => error_code!(E0311),
+        });
 
         '_explain: {
             let (description, span) = match sub.kind() {
@@ -2780,7 +2780,7 @@ impl<'tcx> InferCtxt<'tcx> {
             infer::Nll(..) => bug!("NLL variable found in lexical phase"),
         };
 
-        struct_span_err!(
+        struct_span_code_err!(
             self.tcx.dcx(),
             var_origin.span(),
             E0495,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index e13d1d38a89..d3e28446dde 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -60,7 +60,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
 
     pub fn try_report(&self) -> Option<ErrorGuaranteed> {
         self.try_report_from_nll()
-            .map(|mut diag| diag.emit())
+            .map(|diag| diag.emit())
             .or_else(|| self.try_report_impl_not_conforming_to_trait())
             .or_else(|| self.try_report_anon_anon_conflict())
             .or_else(|| self.try_report_static_impl_trait())
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index ae30cf53c71..02200d6a4aa 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -281,7 +281,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
         };
         if sub.is_error() || sup.is_error() {
-            err.delay_as_bug();
+            err.downgrade_to_delayed_bug();
         }
         err
     }
@@ -367,9 +367,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     &trace.cause.code().peel_derives()
                 {
                     let span = *span;
-                    let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
-                    err.span_note(span, "the lifetime requirement is introduced here");
-                    err
+                    self.report_concrete_failure(placeholder_origin, sub, sup)
+                        .with_span_note(span, "the lifetime requirement is introduced here")
                 } else {
                     unreachable!(
                         "control flow ensures we have a `BindingObligation` or `ExprBindingObligation` here..."
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index fa694f09f17..fcc94687ed2 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -38,7 +38,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtx
 use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
 use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
 use rustc_span::symbol::Symbol;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 
 use std::cell::{Cell, RefCell};
 use std::fmt;
@@ -1434,10 +1434,8 @@ impl<'tcx> InferCtxt<'tcx> {
                     bug!("`{value:?}` is not fully resolved");
                 }
                 if value.has_infer_regions() {
-                    let guar = self
-                        .tcx
-                        .dcx()
-                        .span_delayed_bug(DUMMY_SP, format!("`{value:?}` is not fully resolved"));
+                    let guar =
+                        self.tcx.dcx().delayed_bug(format!("`{value:?}` is not fully resolved"));
                     Ok(self.tcx.fold_regions(value, |re, _| {
                         if re.is_var() { ty::Region::new_error(self.tcx, guar) } else { re }
                     }))
diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs
index c91eb7eddd8..6a684dba8de 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/table.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs
@@ -1,6 +1,5 @@
 use rustc_data_structures::undo_log::UndoLogs;
 use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty};
-use rustc_span::DUMMY_SP;
 
 use crate::infer::{InferCtxtUndoLogs, UndoLog};
 
@@ -40,9 +39,7 @@ impl<'tcx> OpaqueTypeStorage<'tcx> {
 impl<'tcx> Drop for OpaqueTypeStorage<'tcx> {
     fn drop(&mut self) {
         if !self.opaque_types.is_empty() {
-            ty::tls::with(|tcx| {
-                tcx.dcx().span_delayed_bug(DUMMY_SP, format!("{:?}", self.opaque_types))
-            });
+            ty::tls::with(|tcx| tcx.dcx().delayed_bug(format!("{:?}", self.opaque_types)));
         }
     }
 }
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index dd364312cad..7a85268492b 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -175,10 +175,9 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                 // 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.dcx().span_delayed_bug(
-                    rustc_span::DUMMY_SP,
-                    format!("unresolved inference variable in outlives: {v:?}"),
-                );
+                self.tcx
+                    .dcx()
+                    .delayed_bug(format!("unresolved inference variable in outlives: {v:?}"));
                 // add a bound that never holds
                 VerifyBound::AnyBound(vec![])
             }
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 8b31a1118cb..4b254fc7df5 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -180,10 +180,9 @@ impl<'tcx> InferCtxt<'tcx> {
                 &mut OriginalQueryValues::default(),
             );
             self.tcx.check_tys_might_be_eq(canonical).map_err(|_| {
-                self.tcx.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("cannot relate consts of different types (a={a:?}, b={b:?})",),
-                )
+                self.tcx.dcx().delayed_bug(format!(
+                    "cannot relate consts of different types (a={a:?}, b={b:?})",
+                ))
             })
         });
 
@@ -511,7 +510,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
                 ));
             } else {
                 match a_ty.kind() {
-                    &ty::Alias(ty::AliasKind::Projection, data) => {
+                    &ty::Alias(ty::Projection, data) => {
                         // FIXME: This does not handle subtyping correctly, we could
                         // instead create a new inference variable for `a_ty`, emitting
                         // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`.
@@ -523,10 +522,9 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
                         ))
                     }
                     // The old solver only accepts projection predicates for associated types.
-                    ty::Alias(
-                        ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque,
-                        _,
-                    ) => return Err(TypeError::CyclicTy(a_ty)),
+                    ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => {
+                        return Err(TypeError::CyclicTy(a_ty));
+                    }
                     _ => bug!("generalizated `{a_ty:?} to infer, not an alias"),
                 }
             }
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index d89c205da3f..6f218019dee 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -2,9 +2,10 @@ use super::ObjectSafetyViolation;
 
 use crate::infer::InferCtxt;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{struct_span_err, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Map;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::Span;
@@ -19,7 +20,7 @@ impl<'tcx> InferCtxt<'tcx> {
         trait_item_def_id: DefId,
         requirement: &dyn fmt::Display,
     ) -> DiagnosticBuilder<'tcx> {
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.tcx.dcx(),
             error_span,
             E0276,
@@ -42,6 +43,7 @@ impl<'tcx> InferCtxt<'tcx> {
 pub fn report_object_safety_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     span: Span,
+    hir_id: Option<hir::HirId>,
     trait_def_id: DefId,
     violations: &[ObjectSafetyViolation],
 ) -> DiagnosticBuilder<'tcx> {
@@ -50,7 +52,7 @@ pub fn report_object_safety_error<'tcx>(
         hir::Node::Item(item) => Some(item.ident.span),
         _ => None,
     });
-    let mut err = struct_span_err!(
+    let mut err = struct_span_code_err!(
         tcx.dcx(),
         span,
         E0038,
@@ -59,6 +61,24 @@ pub fn report_object_safety_error<'tcx>(
     );
     err.span_label(span, format!("`{trait_str}` cannot be made into an object"));
 
+    if let Some(hir_id) = hir_id
+        && let Some(hir::Node::Ty(ty)) = tcx.hir().find(hir_id)
+        && let hir::TyKind::TraitObject([trait_ref, ..], ..) = ty.kind
+    {
+        let mut hir_id = hir_id;
+        while let hir::Node::Ty(ty) = tcx.hir().get_parent(hir_id) {
+            hir_id = ty.hir_id;
+        }
+        if tcx.hir().get_parent(hir_id).fn_sig().is_some() {
+            // Do not suggest `impl Trait` when dealing with things like super-traits.
+            err.span_suggestion_verbose(
+                ty.span.until(trait_ref.span),
+                "consider using an opaque type instead",
+                "impl ",
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
     let mut reported_violations = FxIndexSet::default();
     let mut multi_span = vec![];
     let mut messages = vec![];
@@ -132,7 +152,10 @@ pub fn report_object_safety_error<'tcx>(
     };
     let externally_visible = if !impls.is_empty()
         && let Some(def_id) = trait_def_id.as_local()
-        && tcx.effective_visibilities(()).is_exported(def_id)
+        // We may be executing this during typeck, which would result in cycle
+        // if we used effective_visibilities query, which looks into opaque types
+        // (and therefore calls typeck).
+        && tcx.resolutions(()).effective_visibilities.is_exported(def_id)
     {
         true
     } else {
diff --git a/compiler/rustc_interface/src/callbacks.rs b/compiler/rustc_interface/src/callbacks.rs
index ef00ced67ff..8c7e49b51f9 100644
--- a/compiler/rustc_interface/src/callbacks.rs
+++ b/compiler/rustc_interface/src/callbacks.rs
@@ -9,7 +9,7 @@
 //! The functions in this file should fall back to the default set in their
 //! origin crate when the `TyCtxt` is not present in TLS.
 
-use rustc_errors::{Diagnostic, TRACK_DIAGNOSTICS};
+use rustc_errors::{Diagnostic, TRACK_DIAGNOSTIC};
 use rustc_middle::dep_graph::{DepNodeExt, TaskDepsRef};
 use rustc_middle::ty::tls;
 use rustc_query_system::dep_graph::dep_node::default_dep_kind_debug;
@@ -29,7 +29,7 @@ fn track_span_parent(def_id: rustc_span::def_id::LocalDefId) {
 /// This is a callback from `rustc_errors` as it cannot access the implicit state
 /// in `rustc_middle` otherwise. It is used when diagnostic messages are
 /// emitted and stores them in the current query, if there is one.
-fn track_diagnostic(diagnostic: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {
+fn track_diagnostic(diagnostic: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
     tls::with_context_opt(|icx| {
         if let Some(icx) = icx {
             if let Some(diagnostics) = icx.diagnostics {
@@ -103,5 +103,5 @@ pub fn setup_callbacks() {
         .swap(&(dep_kind_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
     rustc_query_system::dep_graph::dep_node::DEP_NODE_DEBUG
         .swap(&(dep_node_debug as fn(_, &mut fmt::Formatter<'_>) -> _));
-    TRACK_DIAGNOSTICS.swap(&(track_diagnostic as _));
+    TRACK_DIAGNOSTIC.swap(&(track_diagnostic as _));
 }
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 559874641c3..97956404120 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -254,7 +254,7 @@ fn configure_and_expand(
     }
 
     if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
-        sess.dcx().emit_warning(errors::ProcMacroCratePanicAbort);
+        sess.dcx().emit_warn(errors::ProcMacroCratePanicAbort);
     }
 
     sess.time("maybe_create_a_macro_crate", || {
@@ -735,9 +735,9 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
 
     sess.time("MIR_borrow_checking", || {
         tcx.hir().par_body_owners(|def_id| {
-            // Run THIR unsafety check because it's responsible for stealing
-            // and deallocating THIR when enabled.
-            tcx.ensure().thir_check_unsafety(def_id);
+            // Run unsafety check because it's responsible for stealing and
+            // deallocating THIR.
+            tcx.ensure().check_unsafety(def_id);
             tcx.ensure().mir_borrowck(def_id)
         });
     });
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 07bbe78dc2d..e66ea6f2ca9 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -108,7 +108,7 @@ impl<'tcx> Queries<'tcx> {
 
     pub fn parse(&self) -> Result<QueryResult<'_, ast::Crate>> {
         self.parse.compute(|| {
-            passes::parse(&self.compiler.sess).map_err(|mut parse_error| parse_error.emit())
+            passes::parse(&self.compiler.sess).map_err(|parse_error| parse_error.emit())
         })
     }
 
@@ -213,9 +213,8 @@ impl<'tcx> Queries<'tcx> {
 
                 // Some other attribute.
                 Some(_) => {
-                    tcx.dcx().emit_warning(RustcErrorUnexpectedAnnotation {
-                        span: tcx.def_span(def_id),
-                    });
+                    tcx.dcx()
+                        .emit_warn(RustcErrorUnexpectedAnnotation { span: tcx.def_span(def_id) });
                 }
             }
         }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index c4a1f3a0e51..588139a303c 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -6,8 +6,8 @@ use rustc_session::config::{
     build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
     DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry, ExternLocation, Externs,
     FunctionReturn, InliningThreshold, Input, InstrumentCoverage, InstrumentXRay,
-    LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, MirSpanview, NextSolverConfig,
-    OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius,
+    LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig, OomStrategy,
+    Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius,
     ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
 };
 use rustc_session::lint::Level;
@@ -659,14 +659,12 @@ fn test_unstable_options_tracking_hash() {
     // tidy-alphabetical-start
     untracked!(assert_incr_state, Some(String::from("loaded")));
     untracked!(deduplicate_diagnostics, false);
-    untracked!(dont_buffer_diagnostics, true);
     untracked!(dump_dep_graph, true);
     untracked!(dump_mir, Some(String::from("abc")));
     untracked!(dump_mir_dataflow, true);
     untracked!(dump_mir_dir, String::from("abc"));
     untracked!(dump_mir_exclude_pass_number, true);
     untracked!(dump_mir_graphviz, true);
-    untracked!(dump_mir_spanview, Some(MirSpanview::Statement));
     untracked!(dump_mono_stats, SwitchWithOptPath::Enabled(Some("mono-items-dir/".into())));
     untracked!(dump_mono_stats_format, DumpMonoStatsFormat::Json);
     untracked!(dylib_lto, true);
@@ -700,6 +698,7 @@ fn test_unstable_options_tracking_hash() {
     untracked!(query_dep_graph, true);
     untracked!(self_profile, SwitchWithOptPath::Enabled(None));
     untracked!(self_profile_events, Some(vec![String::new()]));
+    untracked!(shell_argfiles, true);
     untracked!(span_debug, true);
     untracked!(span_free_formats, true);
     untracked!(temps_dir, Some(String::from("abc")));
@@ -806,7 +805,6 @@ fn test_unstable_options_tracking_hash() {
     tracked!(relax_elf_relocations, Some(true));
     tracked!(relro_level, Some(RelroLevel::Full));
     tracked!(remap_cwd_prefix, Some(PathBuf::from("abc")));
-    tracked!(report_delayed_bugs, true);
     tracked!(sanitizer, SanitizerSet::ADDRESS);
     tracked!(sanitizer_cfi_canonical_jump_tables, None);
     tracked!(sanitizer_cfi_generalize_pointers, Some(true));
@@ -822,7 +820,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(stack_protector, StackProtector::All);
     tracked!(teach, true);
     tracked!(thinlto, Some(true));
-    tracked!(thir_unsafeck, true);
+    tracked!(thir_unsafeck, false);
     tracked!(tiny_const_eval_limit, true);
     tracked!(tls_model, Some(TlsModel::GeneralDynamic));
     tracked!(translate_remapped_path_to_local_path, false);
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 92a6445ed09..9fd44e46b31 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -431,7 +431,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
 
     base.retain(|crate_type| {
         if output::invalid_output_for_target(session, *crate_type) {
-            session.dcx().emit_warning(errors::UnsupportedCrateTypeForTarget {
+            session.dcx().emit_warn(errors::UnsupportedCrateTypeForTarget {
                 crate_type: *crate_type,
                 target_triple: &session.opts.target_triple,
             });
@@ -507,16 +507,16 @@ pub fn build_output_filenames(attrs: &[ast::Attribute], sess: &Session) -> Outpu
             let unnamed_output_types =
                 sess.opts.output_types.values().filter(|a| a.is_none()).count();
             let ofile = if unnamed_output_types > 1 {
-                sess.dcx().emit_warning(errors::MultipleOutputTypesAdaption);
+                sess.dcx().emit_warn(errors::MultipleOutputTypesAdaption);
                 None
             } else {
                 if !sess.opts.cg.extra_filename.is_empty() {
-                    sess.dcx().emit_warning(errors::IgnoringExtraFilename);
+                    sess.dcx().emit_warn(errors::IgnoringExtraFilename);
                 }
                 Some(out_file.clone())
             };
             if sess.io.output_dir != None {
-                sess.dcx().emit_warning(errors::IgnoringOutDir);
+                sess.dcx().emit_warn(errors::IgnoringOutDir);
             }
 
             let out_filestem =
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 8afb5f2d32e..8d2f2aaca55 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -2734,10 +2734,13 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
     #[allow(rustc::diagnostic_outside_of_impl)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
         if let hir::Expr {
-            kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }),
+            kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, options, .. }),
             ..
         } = expr
         {
+            // asm with `options(raw)` does not do replacement with `{` and `}`.
+            let raw = options.contains(InlineAsmOptions::RAW);
+
             for (template_sym, template_snippet, template_span) in template_strs.iter() {
                 let template_str = template_sym.as_str();
                 let find_label_span = |needle: &str| -> Option<Span> {
@@ -2763,24 +2766,57 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
                 for statement in statements {
                     // If there's a comment, trim it from the statement
                     let statement = statement.find("//").map_or(statement, |idx| &statement[..idx]);
+
+                    // In this loop, if there is ever a non-label, no labels can come after it.
                     let mut start_idx = 0;
-                    for (idx, _) in statement.match_indices(':') {
+                    'label_loop: for (idx, _) in statement.match_indices(':') {
                         let possible_label = statement[start_idx..idx].trim();
                         let mut chars = possible_label.chars();
-                        let Some(c) = chars.next() else {
-                            // Empty string means a leading ':' in this section, which is not a label
-                            break;
+
+                        let Some(start) = chars.next() else {
+                            // Empty string means a leading ':' in this section, which is not a label.
+                            break 'label_loop;
                         };
-                        // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
-                        if (c.is_alphabetic() || matches!(c, '.' | '_'))
-                            && chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
-                        {
-                            found_labels.push(possible_label);
-                        } else {
-                            // If we encounter a non-label, there cannot be any further labels, so stop checking
-                            break;
+
+                        // Whether a { bracket has been seen and its } hasn't been found yet.
+                        let mut in_bracket = false;
+
+                        // A label starts with an ASCII alphabetic character or . or _
+                        // A label can also start with a format arg, if it's not a raw asm block.
+                        if !raw && start == '{' {
+                            in_bracket = true;
+                        } else if !(start.is_ascii_alphabetic() || matches!(start, '.' | '_')) {
+                            break 'label_loop;
+                        }
+
+                        // Labels continue with ASCII alphanumeric characters, _, or $
+                        for c in chars {
+                            // Inside a template format arg, any character is permitted for the puproses of label detection
+                            // because we assume that it can be replaced with some other valid label string later.
+                            // `options(raw)` asm blocks cannot have format args, so they are excluded from this special case.
+                            if !raw && in_bracket {
+                                if c == '{' {
+                                    // Nested brackets are not allowed in format args, this cannot be a label.
+                                    break 'label_loop;
+                                }
+
+                                if c == '}' {
+                                    // The end of the format arg.
+                                    in_bracket = false;
+                                }
+                            } else if !raw && c == '{' {
+                                // Start of a format arg.
+                                in_bracket = true;
+                            } else {
+                                if !(c.is_ascii_alphanumeric() || matches!(c, '_' | '$')) {
+                                    // The potential label had an invalid character inside it, it cannot be a label.
+                                    break 'label_loop;
+                                }
+                            }
                         }
 
+                        // If all characters passed the label checks, this is likely a label.
+                        found_labels.push(possible_label);
                         start_idx = idx + 1;
                     }
                 }
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index eccea35c702..841d282a099 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -31,7 +31,7 @@ impl AddToDiagnostic for OverruledAttributeSub {
         match self {
             OverruledAttributeSub::DefaultSource { id } => {
                 diag.note(fluent::lint_default_source);
-                diag.set_arg("id", id);
+                diag.arg("id", id);
             }
             OverruledAttributeSub::NodeSource { span, reason } => {
                 diag.span_label(span, fluent::lint_node_source);
diff --git a/compiler/rustc_lint/src/foreign_modules.rs b/compiler/rustc_lint/src/foreign_modules.rs
index 31d9c0d33fe..ecb7a157f39 100644
--- a/compiler/rustc_lint/src/foreign_modules.rs
+++ b/compiler/rustc_lint/src/foreign_modules.rs
@@ -1,5 +1,5 @@
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_middle::query::Providers;
@@ -72,7 +72,7 @@ struct ClashingExternDeclarations {
     /// the symbol should be reported as a clashing declaration.
     // FIXME: Technically, we could just store a &'tcx str here without issue; however, the
     // `impl_lint_pass` macro doesn't currently support lints parametric over a lifetime.
-    seen_decls: FxHashMap<Symbol, hir::OwnerId>,
+    seen_decls: UnordMap<Symbol, hir::OwnerId>,
 }
 
 /// Differentiate between whether the name for an extern decl came from the link_name attribute or
@@ -96,7 +96,7 @@ impl SymbolName {
 
 impl ClashingExternDeclarations {
     pub(crate) fn new() -> Self {
-        ClashingExternDeclarations { seen_decls: FxHashMap::default() }
+        ClashingExternDeclarations { seen_decls: Default::default() }
     }
 
     /// Insert a new foreign item into the seen set. If a symbol with the same name already exists
@@ -209,12 +209,12 @@ fn structurally_same_type<'tcx>(
     b: Ty<'tcx>,
     ckind: types::CItemKind,
 ) -> bool {
-    let mut seen_types = FxHashSet::default();
+    let mut seen_types = UnordSet::default();
     structurally_same_type_impl(&mut seen_types, tcx, param_env, a, b, ckind)
 }
 
 fn structurally_same_type_impl<'tcx>(
-    seen_types: &mut FxHashSet<(Ty<'tcx>, Ty<'tcx>)>,
+    seen_types: &mut UnordSet<(Ty<'tcx>, Ty<'tcx>)>,
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     a: Ty<'tcx>,
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 53d99c7f7f3..e3405aa2e55 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -549,7 +549,9 @@ declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);
 
 impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
-        if let ExprKind::Binary(BinOp { node: BinOpKind::Eq, .. }, lhs, rhs) = expr.kind {
+        if let ExprKind::Binary(BinOp { node: BinOpKind::Eq | BinOpKind::Ne, .. }, lhs, rhs) =
+            expr.kind
+        {
             if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
                 cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
             }
diff --git a/compiler/rustc_lint/src/let_underscore.rs b/compiler/rustc_lint/src/let_underscore.rs
index 3eefd1b0e08..bdace8e01f6 100644
--- a/compiler/rustc_lint/src/let_underscore.rs
+++ b/compiler/rustc_lint/src/let_underscore.rs
@@ -108,6 +108,10 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
         if !matches!(local.pat.kind, hir::PatKind::Wild) {
             return;
         }
+
+        if matches!(local.source, rustc_hir::LocalSource::AsyncFn) {
+            return;
+        }
         if let Some(init) = local.init {
             let init_ty = cx.typeck_results().expr_ty(init);
             // If the type has a trivial Drop implementation, then it doesn't
@@ -126,6 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                 suggestion: local.pat.span,
                 multi_suggestion_start: local.span.until(init.span),
                 multi_suggestion_end: init.span.shrink_to_hi(),
+                is_assign_desugar: matches!(local.source, rustc_hir::LocalSource::AssignDesugar(_)),
             };
             if is_sync_lock {
                 let mut span = MultiSpan::from_spans(vec![local.pat.span, init.span]);
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 5950bc76ade..49821437b76 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1069,7 +1069,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                         Some(span.into()),
                         fluent::lint_unknown_gated_lint,
                         |lint| {
-                            lint.set_arg("name", lint_id.lint.name_lower());
+                            lint.arg("name", lint_id.lint.name_lower());
                             lint.note(fluent::lint_note);
                             rustc_session::parse::add_feature_diagnostics_for_issue(
                                 lint,
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 93904fb5c56..a9996e4a155 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -328,6 +328,7 @@ fn register_builtins(store: &mut LintStore) {
     store.register_renamed("disjoint_capture_migration", "rust_2021_incompatible_closure_captures");
     store.register_renamed("or_patterns_back_compat", "rust_2021_incompatible_or_patterns");
     store.register_renamed("non_fmt_panic", "non_fmt_panics");
+    store.register_renamed("unused_tuple_struct_fields", "dead_code");
 
     // These were moved to tool lints, but rustc still sees them when compiling normally, before
     // tool lints are registered, so `check_tool_name_for_backwards_compat` doesn't work. Use
@@ -512,6 +513,11 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, see PR #117984 \
         <https://github.com/rust-lang/rust/pull/117984> for more information",
     );
+    store.register_removed(
+        "coinductive_overlap_in_coherence",
+        "converted into hard error, see PR #118649 \
+         <https://github.com/rust-lang/rust/pull/118649> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index ca6408bdf3d..bc9a9d7b745 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -5,8 +5,8 @@ use std::num::NonZeroU32;
 use crate::errors::RequestedLevel;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    AddToDiagnostic, Applicability, DecorateLint, DiagnosticMessage, DiagnosticStyledString,
-    SuggestionStyle,
+    AddToDiagnostic, Applicability, DecorateLint, Diagnostic, DiagnosticBuilder, DiagnosticMessage,
+    DiagnosticStyledString, SubdiagnosticMessage, SuggestionStyle,
 };
 use rustc_hir::def_id::DefId;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
@@ -135,7 +135,7 @@ pub struct BuiltinMissingDebugImpl<'a> {
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for BuiltinMissingDebugImpl<'_> {
     fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("debug", self.tcx.def_path_str(self.def_id));
+        diag.arg("debug", self.tcx.def_path_str(self.def_id));
     }
 
     fn msg(&self) -> DiagnosticMessage {
@@ -239,7 +239,7 @@ pub struct BuiltinUngatedAsyncFnTrackCaller<'a> {
 }
 
 impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
         diag.span_label(self.label, fluent::lint_label);
         rustc_session::parse::add_feature_diagnostics(
             diag,
@@ -268,12 +268,9 @@ pub struct SuggestChangingAssocTypes<'a, 'b> {
 }
 
 impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
         // bound. Let's see if this type does that.
@@ -281,7 +278,7 @@ impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
         // We use a HIR visitor to walk the type.
         use rustc_hir::intravisit::{self, Visitor};
         struct WalkAssocTypes<'a> {
-            err: &'a mut rustc_errors::Diagnostic,
+            err: &'a mut Diagnostic,
         }
         impl Visitor<'_> for WalkAssocTypes<'_> {
             fn visit_qpath(
@@ -326,12 +323,9 @@ pub struct BuiltinTypeAliasGenericBoundsSuggestion {
 }
 
 impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         diag.multipart_suggestion(
             fluent::lint_suggestion,
@@ -425,8 +419,8 @@ pub struct BuiltinUnpermittedTypeInit<'a> {
 }
 
 impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("ty", self.ty);
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("ty", self.ty);
         diag.span_label(self.label, fluent::lint_builtin_unpermitted_type_init_label);
         if let InhabitedPredicate::True = self.ty.inhabited_predicate(self.tcx) {
             // Only suggest late `MaybeUninit::assume_init` initialization if the type is inhabited.
@@ -438,7 +432,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> {
         self.sub.add_to_diagnostic(diag);
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         self.msg.clone()
     }
 }
@@ -449,12 +443,9 @@ pub struct BuiltinUnpermittedTypeInitSub {
 }
 
 impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         let mut err = self.err;
         loop {
@@ -506,12 +497,9 @@ pub struct BuiltinClashingExternSub<'a> {
 }
 
 impl AddToDiagnostic for BuiltinClashingExternSub<'_> {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         let mut expected_str = DiagnosticStyledString::new();
         expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
@@ -779,12 +767,9 @@ pub struct HiddenUnicodeCodepointsDiagLabels {
 }
 
 impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         for (c, span) in self.spans {
             diag.span_label(span, format!("{c:?}"));
@@ -799,12 +784,9 @@ pub enum HiddenUnicodeCodepointsDiagSub {
 
 // Used because of multiple multipart_suggestion and note
 impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         match self {
             HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
@@ -830,7 +812,7 @@ impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
                 // FIXME: in other suggestions we've reversed the inner spans of doc comments. We
                 // should do the same here to provide the same good suggestions as we do for
                 // literals above.
-                diag.set_arg(
+                diag.arg(
                     "escaped",
                     spans
                         .into_iter()
@@ -950,20 +932,19 @@ pub struct NonBindingLetSub {
     pub suggestion: Span,
     pub multi_suggestion_start: Span,
     pub multi_suggestion_end: Span,
+    pub is_assign_desugar: bool,
 }
 
 impl AddToDiagnostic for NonBindingLetSub {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
+        let prefix = if self.is_assign_desugar { "let " } else { "" };
         diag.span_suggestion_verbose(
             self.suggestion,
             fluent::lint_non_binding_let_suggestion,
-            "_unused",
+            format!("{prefix}_unused"),
             Applicability::MachineApplicable,
         );
         diag.multipart_suggestion(
@@ -1147,8 +1128,8 @@ pub struct NonFmtPanicUnused {
 
 // Used because of two suggestions based on one Option<Span>
 impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("count", self.count);
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("count", self.count);
         diag.note(fluent::lint_note);
         if let Some(span) = self.suggestion {
             diag.span_suggestion(
@@ -1166,7 +1147,7 @@ impl<'a> DecorateLint<'a, ()> for NonFmtPanicUnused {
         }
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_non_fmt_panic_unused
     }
 }
@@ -1224,12 +1205,9 @@ pub enum NonSnakeCaseDiagSub {
 }
 
 impl AddToDiagnostic for NonSnakeCaseDiagSub {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         match self {
             NonSnakeCaseDiagSub::Label { span } => {
@@ -1342,12 +1320,12 @@ pub struct DropTraitConstraintsDiag<'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropTraitConstraintsDiag<'_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("predicate", self.predicate);
-        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id));
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("predicate", self.predicate);
+        diag.arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_drop_trait_constraints
     }
 }
@@ -1359,11 +1337,11 @@ pub struct DropGlue<'a> {
 
 // Needed for def_path_str
 impl<'a> DecorateLint<'a, ()> for DropGlue<'_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("needs_drop", self.tcx.def_path_str(self.def_id));
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("needs_drop", self.tcx.def_path_str(self.def_id));
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_drop_glue
     }
 }
@@ -1423,12 +1401,9 @@ pub enum OverflowingBinHexSign {
 }
 
 impl AddToDiagnostic for OverflowingBinHexSign {
-    fn add_to_diagnostic_with<F>(self, diag: &mut rustc_errors::Diagnostic, _: F)
+    fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
     where
-        F: Fn(
-            &mut rustc_errors::Diagnostic,
-            rustc_errors::SubdiagnosticMessage,
-        ) -> rustc_errors::SubdiagnosticMessage,
+        F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
         match self {
             OverflowingBinHexSign::Positive => {
@@ -1633,9 +1608,9 @@ pub struct ImproperCTypes<'a> {
 
 // Used because of the complexity of Option<DiagnosticMessage>, DiagnosticMessage, and Option<Span>
 impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("ty", self.ty);
-        diag.set_arg("desc", self.desc);
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("ty", self.ty);
+        diag.arg("desc", self.desc);
         diag.span_label(self.label, fluent::lint_label);
         if let Some(help) = self.help {
             diag.help(help);
@@ -1646,7 +1621,7 @@ impl<'a> DecorateLint<'a, ()> for ImproperCTypes<'_> {
         }
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_improper_ctypes
     }
 }
@@ -1776,10 +1751,10 @@ pub enum UnusedDefSuggestion {
 
 // Needed because of def_path_str
 impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
-        diag.set_arg("pre", self.pre);
-        diag.set_arg("post", self.post);
-        diag.set_arg("def", self.cx.tcx.def_path_str(self.def_id));
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
+        diag.arg("pre", self.pre);
+        diag.arg("post", self.post);
+        diag.arg("def", self.cx.tcx.def_path_str(self.def_id));
         // check for #[must_use = "..."]
         if let Some(note) = self.note {
             diag.note(note.to_string());
@@ -1789,7 +1764,7 @@ impl<'a> DecorateLint<'a, ()> for UnusedDef<'_, '_> {
         }
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_unused_def
     }
 }
@@ -1859,14 +1834,14 @@ pub struct AsyncFnInTraitDiag {
 }
 
 impl<'a> DecorateLint<'a, ()> for AsyncFnInTraitDiag {
-    fn decorate_lint<'b>(self, diag: &'b mut rustc_errors::DiagnosticBuilder<'a, ()>) {
+    fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
         diag.note(fluent::lint_note);
         if let Some(sugg) = self.sugg {
             diag.multipart_suggestion(fluent::lint_suggestion, sugg, Applicability::MaybeIncorrect);
         }
     }
 
-    fn msg(&self) -> rustc_errors::DiagnosticMessage {
+    fn msg(&self) -> DiagnosticMessage {
         fluent::lint_async_fn_in_trait
     }
 }
diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs
index 08b2bf6af37..3405dd3a916 100644
--- a/compiler/rustc_lint/src/non_ascii_idents.rs
+++ b/compiler/rustc_lint/src/non_ascii_idents.rs
@@ -4,7 +4,8 @@ use crate::lints::{
 };
 use crate::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordMap;
 use rustc_span::symbol::Symbol;
 
 declare_lint! {
@@ -194,8 +195,8 @@ impl EarlyLintPass for NonAsciiIdents {
         }
 
         if has_non_ascii_idents && check_confusable_idents {
-            let mut skeleton_map: FxHashMap<Symbol, (Symbol, Span, bool)> =
-                FxHashMap::with_capacity_and_hasher(symbols.len(), Default::default());
+            let mut skeleton_map: UnordMap<Symbol, (Symbol, Span, bool)> =
+                UnordMap::with_capacity(symbols.len());
             let mut skeleton_buf = String::new();
 
             for (&symbol, &sp) in symbols.iter() {
@@ -248,8 +249,8 @@ impl EarlyLintPass for NonAsciiIdents {
                 Verified,
             }
 
-            let mut script_states: FxHashMap<AugmentedScriptSet, ScriptSetUsage> =
-                FxHashMap::default();
+            let mut script_states: FxIndexMap<AugmentedScriptSet, ScriptSetUsage> =
+                Default::default();
             let latin_augmented_script_set = AugmentedScriptSet::for_char('A');
             script_states.insert(latin_augmented_script_set, ScriptSetUsage::Verified);
 
diff --git a/compiler/rustc_lint/src/non_fmt_panic.rs b/compiler/rustc_lint/src/non_fmt_panic.rs
index 9fcd70ba0b5..479acd88d71 100644
--- a/compiler/rustc_lint/src/non_fmt_panic.rs
+++ b/compiler/rustc_lint/src/non_fmt_panic.rs
@@ -111,17 +111,18 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
     let mut arg_span = arg.span;
     let mut arg_macro = None;
     while !span.contains(arg_span) {
-        let expn = arg_span.ctxt().outer_expn_data();
-        if expn.is_root() {
+        let ctxt = arg_span.ctxt();
+        if ctxt.is_root() {
             break;
         }
+        let expn = ctxt.outer_expn_data();
         arg_macro = expn.macro_def_id;
         arg_span = expn.call_site;
     }
 
     #[allow(rustc::diagnostic_outside_of_impl)]
     cx.struct_span_lint(NON_FMT_PANICS, arg_span, fluent::lint_non_fmt_panic, |lint| {
-        lint.set_arg("name", symbol);
+        lint.arg("name", symbol);
         lint.note(fluent::lint_note);
         lint.note(fluent::lint_more_info_note);
         if !is_arg_inside_call(arg_span, span) {
@@ -180,7 +181,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
                     fmt_applicability,
                 );
             } else if suggest_debug {
-                lint.set_arg("ty", ty);
+                lint.arg("ty", ty);
                 lint.span_suggestion_verbose(
                     arg_span.shrink_to_lo(),
                     fluent::lint_debug_suggestion,
@@ -191,7 +192,7 @@ fn check_panic<'tcx>(cx: &LateContext<'tcx>, f: &'tcx hir::Expr<'tcx>, arg: &'tc
 
             if suggest_panic_any {
                 if let Some((open, close, del)) = find_delimiters(cx, span) {
-                    lint.set_arg("already_suggested", suggest_display || suggest_debug);
+                    lint.arg("already_suggested", suggest_display || suggest_debug);
                     lint.multipart_suggestion(
                         fluent::lint_panic_suggestion,
                         if del == '(' {
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 6dade43a183..a86fe2db2b2 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -29,7 +29,6 @@ use rustc_span::{Span, Symbol};
 use rustc_target::abi::{Abi, Size, WrappingRange};
 use rustc_target::abi::{Integer, TagEncoding, Variants};
 use rustc_target::spec::abi::Abi as SpecAbi;
-use rustc_type_ir::DynKind;
 
 use std::iter;
 use std::ops::ControlFlow;
@@ -675,7 +674,7 @@ fn lint_wide_pointer<'tcx>(
         }
         match ty.kind() {
             ty::RawPtr(TypeAndMut { mutbl: _, ty }) => (!ty.is_sized(cx.tcx, cx.param_env))
-                .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, DynKind::Dyn)))),
+                .then(|| (refs, matches!(ty.kind(), ty::Dynamic(_, _, ty::Dyn)))),
             _ => None,
         }
     };
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index e35d1ee0461..e917e7cb02b 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -26,7 +26,6 @@ declare_lint_pass! {
         BYTE_SLICE_IN_PACKED_STRUCT_WITH_DERIVE,
         CENUM_IMPL_DROP_CAST,
         COHERENCE_LEAK_CHECK,
-        COINDUCTIVE_OVERLAP_IN_COHERENCE,
         CONFLICTING_REPR_HINTS,
         CONST_EVALUATABLE_UNCHECKED,
         CONST_ITEM_MUTATION,
@@ -89,6 +88,7 @@ declare_lint_pass! {
         SINGLE_USE_LIFETIMES,
         SOFT_UNSTABLE,
         STABLE_FEATURES,
+        STATIC_MUT_REF,
         SUSPICIOUS_AUTO_TRAIT_IMPLS,
         TEST_UNSTABLE_LINT,
         TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
@@ -125,7 +125,6 @@ declare_lint_pass! {
         UNUSED_MACROS,
         UNUSED_MUT,
         UNUSED_QUALIFICATIONS,
-        UNUSED_TUPLE_STRUCT_FIELDS,
         UNUSED_UNSAFE,
         UNUSED_VARIABLES,
         USELESS_DEPRECATED,
@@ -697,8 +696,13 @@ declare_lint! {
     /// Dead code may signal a mistake or unfinished code. To silence the
     /// warning for individual items, prefix the name with an underscore such
     /// as `_foo`. If it was intended to expose the item outside of the crate,
-    /// consider adding a visibility modifier like `pub`. Otherwise consider
-    /// removing the unused code.
+    /// consider adding a visibility modifier like `pub`.
+    ///
+    /// To preserve the numbering of tuple structs with unused fields,
+    /// change the unused fields to have unit type or use
+    /// `PhantomData`.
+    ///
+    /// Otherwise consider removing the unused code.
     pub DEAD_CODE,
     Warn,
     "detect unused, unexported items"
@@ -733,32 +737,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `unused_tuple_struct_fields` lint detects fields of tuple structs
-    /// that are never read.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// #[warn(unused_tuple_struct_fields)]
-    /// struct S(i32, i32, i32);
-    /// let s = S(1, 2, 3);
-    /// let _ = (s.0, s.2);
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// Tuple struct fields that are never read anywhere may indicate a
-    /// mistake or unfinished code. To silence this warning, consider
-    /// removing the unused field(s) or, to preserve the numbering of the
-    /// remaining fields, change the unused field(s) to have unit type.
-    pub UNUSED_TUPLE_STRUCT_FIELDS,
-    Allow,
-    "detects tuple struct fields that are never read"
-}
-
-declare_lint! {
     /// The `unreachable_code` lint detects unreachable code paths.
     ///
     /// ### Example
@@ -1790,6 +1768,57 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `static_mut_ref` lint checks for shared or mutable references
+    /// of mutable static inside `unsafe` blocks and `unsafe` functions.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021
+    /// fn main() {
+    ///     static mut X: i32 = 23;
+    ///     static mut Y: i32 = 24;
+    ///
+    ///     unsafe {
+    ///         let y = &X;
+    ///         let ref x = X;
+    ///         let (x, y) = (&X, &Y);
+    ///         foo(&X);
+    ///     }
+    /// }
+    ///
+    /// unsafe fn _foo() {
+    ///     static mut X: i32 = 23;
+    ///     static mut Y: i32 = 24;
+    ///
+    ///     let y = &X;
+    ///     let ref x = X;
+    ///     let (x, y) = (&X, &Y);
+    ///     foo(&X);
+    /// }
+    ///
+    /// fn foo<'a>(_x: &'a i32) {}
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Shared or mutable references of mutable static are almost always a mistake and
+    /// can lead to undefined behavior and various other problems in your code.
+    ///
+    /// This lint is "warn" by default on editions up to 2021, from 2024 there is
+    /// a hard error instead.
+    pub STATIC_MUT_REF,
+    Warn,
+    "shared references or mutable references of mutable static is discouraged",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "issue #114447 <https://github.com/rust-lang/rust/issues/114447>",
+        explain_reason: false,
+    };
+}
+
+declare_lint! {
     /// The `absolute_paths_not_starting_with_crate` lint detects fully
     /// qualified paths that start with a module name instead of `crate`,
     /// `self`, or an extern crate name
@@ -4390,45 +4419,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `coinductive_overlap_in_coherence` lint detects impls which are currently
-    /// considered not overlapping, but may be considered to overlap if support for
-    /// coinduction is added to the trait solver.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail
-    /// #![deny(coinductive_overlap_in_coherence)]
-    ///
-    /// trait CyclicTrait {}
-    /// impl<T: CyclicTrait> CyclicTrait for T {}
-    ///
-    /// trait Trait {}
-    /// impl<T: CyclicTrait> Trait for T {}
-    /// // conflicting impl with the above
-    /// impl Trait for u8 {}
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// We have two choices for impl which satisfy `u8: Trait`: the blanket impl
-    /// for generic `T`, and the direct impl for `u8`. These two impls nominally
-    /// overlap, since we can infer `T = u8` in the former impl, but since the where
-    /// clause `u8: CyclicTrait` would end up resulting in a cycle (since it depends
-    /// on itself), the blanket impl is not considered to hold for `u8`. This will
-    /// change in a future release.
-    pub COINDUCTIVE_OVERLAP_IN_COHERENCE,
-    Deny,
-    "impls that are not considered to overlap may be considered to \
-    overlap in the future",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
-        reference: "issue #114040 <https://github.com/rust-lang/rust/issues/114040>",
-    };
-}
-
-declare_lint! {
     /// The `unknown_or_malformed_diagnostic_attributes` lint detects unrecognized or otherwise malformed
     /// diagnostic attributes.
     ///
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index a25cfe68e0d..eed35326c45 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -9,7 +9,9 @@ pub use self::Level::*;
 use rustc_ast::node_id::NodeId;
 use rustc_ast::{AttrId, Attribute};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
+use rustc_data_structures::stable_hasher::{
+    HashStable, StableCompare, StableHasher, ToStableHashKey,
+};
 use rustc_error_messages::{DiagnosticMessage, MultiSpan};
 use rustc_hir::HashStableContext;
 use rustc_hir::HirId;
@@ -541,6 +543,14 @@ impl<HCX> ToStableHashKey<HCX> for LintId {
     }
 }
 
+impl StableCompare for LintId {
+    const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.lint_name_raw().cmp(&other.lint_name_raw())
+    }
+}
+
 #[derive(Debug)]
 pub struct AmbiguityErrorDiag {
     pub msg: String,
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index cf3f526400d..76eb6bfaef7 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -787,7 +787,9 @@ LLVMRustOptimize(
     for (auto PluginPath: Plugins) {
       auto Plugin = PassPlugin::Load(PluginPath.str());
       if (!Plugin) {
-        LLVMRustSetLastError(("Failed to load pass plugin" + PluginPath.str()).c_str());
+        auto Err = Plugin.takeError();
+        auto ErrMsg = llvm::toString(std::move(Err));
+        LLVMRustSetLastError(ErrMsg.c_str());
         return LLVMRustResult::Failure;
       }
       Plugin->registerPassBuilderCallbacks(PB);
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index f3e98d68b60..fb908fe2db1 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -5,8 +5,8 @@ use crate::diagnostics::error::{
 };
 use crate::diagnostics::utils::{
     build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
-    should_generate_set_arg, type_is_bool, type_is_unit, type_matches_path, FieldInfo,
-    FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
+    should_generate_arg, type_is_bool, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy,
+    FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
 };
 use proc_macro2::{Ident, Span, TokenStream};
 use quote::{format_ident, quote, quote_spanned};
@@ -125,15 +125,15 @@ impl DiagnosticDeriveVariantBuilder {
     }
 
     /// Generates calls to `span_label` and similar functions based on the attributes on fields or
-    /// calls to `set_arg` when no attributes are present.
+    /// calls to `arg` when no attributes are present.
     pub(crate) fn body(&mut self, variant: &VariantInfo<'_>) -> TokenStream {
         let mut body = quote! {};
-        // Generate `set_arg` calls first..
-        for binding in variant.bindings().iter().filter(|bi| should_generate_set_arg(bi.ast())) {
+        // Generate `arg` calls first..
+        for binding in variant.bindings().iter().filter(|bi| should_generate_arg(bi.ast())) {
             body.extend(self.generate_field_code(binding));
         }
         // ..and then subdiagnostic additions.
-        for binding in variant.bindings().iter().filter(|bi| !should_generate_set_arg(bi.ast())) {
+        for binding in variant.bindings().iter().filter(|bi| !should_generate_arg(bi.ast())) {
             body.extend(self.generate_field_attrs_code(binding));
         }
         body
@@ -253,7 +253,7 @@ impl DiagnosticDeriveVariantBuilder {
         let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
 
         quote! {
-            diag.set_arg(
+            diag.arg(
                 stringify!(#ident),
                 #field_binding
             );
@@ -312,7 +312,7 @@ impl DiagnosticDeriveVariantBuilder {
         let name = ident.to_string();
         match (&attr.meta, name.as_str()) {
             // Don't need to do anything - by virtue of the attribute existing, the
-            // `set_arg` call will not be generated.
+            // `arg` call will not be generated.
             (Meta::Path(_), "skip_arg") => return Ok(quote! {}),
             (Meta::Path(_), "primary_span") => {
                 match self.kind {
@@ -320,7 +320,7 @@ impl DiagnosticDeriveVariantBuilder {
                         report_error_if_not_applied_to_span(attr, &info)?;
 
                         return Ok(quote! {
-                            diag.set_span(#binding);
+                            diag.span(#binding);
                         });
                     }
                     DiagnosticDeriveKind::LintDiagnostic => {
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 663abecb67c..c029b931e7d 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -6,8 +6,8 @@ use crate::diagnostics::error::{
 use crate::diagnostics::utils::{
     build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
     report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
-    should_generate_set_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap,
-    HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
+    should_generate_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap,
+    SetOnce, SpannedOption, SubdiagnosticKind,
 };
 use proc_macro2::TokenStream;
 use quote::{format_ident, quote};
@@ -214,7 +214,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
     }
 
     /// Generates the code for a field with no attributes.
-    fn generate_field_set_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
+    fn generate_field_arg(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {
         let diag = &self.parent.diag;
 
         let field = binding_info.ast();
@@ -225,7 +225,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
         let ident = format_ident!("{}", ident); // strip `r#` prefix, if present
 
         quote! {
-            #diag.set_arg(
+            #diag.arg(
                 stringify!(#ident),
                 #field_binding
             );
@@ -505,7 +505,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
             .variant
             .bindings()
             .iter()
-            .filter(|binding| !should_generate_set_arg(binding.ast()))
+            .filter(|binding| !should_generate_arg(binding.ast()))
             .map(|binding| self.generate_field_attr_code(binding, kind_stats))
             .collect();
 
@@ -593,8 +593,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
             .variant
             .bindings()
             .iter()
-            .filter(|binding| should_generate_set_arg(binding.ast()))
-            .map(|binding| self.generate_field_set_arg(binding))
+            .filter(|binding| should_generate_arg(binding.ast()))
+            .map(|binding| self.generate_field_arg(binding))
             .collect();
 
         let formatting_init = &self.formatting_init;
diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs
index 2700f02e33a..4684306e235 100644
--- a/compiler/rustc_macros/src/diagnostics/utils.rs
+++ b/compiler/rustc_macros/src/diagnostics/utils.rs
@@ -584,7 +584,7 @@ pub(super) enum SubdiagnosticKind {
         suggestion_kind: SuggestionKind,
         applicability: SpannedOption<Applicability>,
         /// Identifier for variable used for formatted code, e.g. `___code_0`. Enables separation
-        /// of formatting and diagnostic emission so that `set_arg` calls can happen in-between..
+        /// of formatting and diagnostic emission so that `arg` calls can happen in-between..
         code_field: syn::Ident,
         /// Initialization logic for `code_field`'s variable, e.g.
         /// `let __formatted_code = /* whatever */;`
@@ -863,9 +863,9 @@ impl quote::IdentFragment for SubdiagnosticKind {
     }
 }
 
-/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic
+/// Returns `true` if `field` should generate a `arg` call rather than any other diagnostic
 /// call (like `span_label`).
-pub(super) fn should_generate_set_arg(field: &Field) -> bool {
+pub(super) fn should_generate_arg(field: &Field) -> bool {
     // Perhaps this should be an exhaustive list...
     field.attrs.iter().all(|attr| is_doc_comment(attr))
 }
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index f558b74be9a..f5d942b924e 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -56,6 +56,8 @@ decl_derive!(
     hash_stable::hash_stable_no_context_derive
 );
 
+decl_derive!([Decodable_Generic] => serialize::decodable_generic_derive);
+decl_derive!([Encodable_Generic] => serialize::encodable_generic_derive);
 decl_derive!([Decodable] => serialize::decodable_derive);
 decl_derive!([Encodable] => serialize::encodable_derive);
 decl_derive!([TyDecodable] => serialize::type_decodable_derive);
diff --git a/compiler/rustc_macros/src/serialize.rs b/compiler/rustc_macros/src/serialize.rs
index 047066ac681..9ca7ce09ba6 100644
--- a/compiler/rustc_macros/src/serialize.rs
+++ b/compiler/rustc_macros/src/serialize.rs
@@ -32,6 +32,14 @@ pub fn meta_decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:
 
 pub fn decodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let decoder_ty = quote! { __D };
+    s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_span::SpanDecoder});
+    s.add_bounds(synstructure::AddBounds::Generics);
+
+    decodable_body(s, decoder_ty)
+}
+
+pub fn decodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    let decoder_ty = quote! { __D };
     s.add_impl_generic(parse_quote! {#decoder_ty: ::rustc_serialize::Decoder});
     s.add_bounds(synstructure::AddBounds::Generics);
 
@@ -130,6 +138,14 @@ pub fn meta_encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2:
 
 pub fn encodable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
     let encoder_ty = quote! { __E };
+    s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_span::SpanEncoder});
+    s.add_bounds(synstructure::AddBounds::Generics);
+
+    encodable_body(s, encoder_ty, false)
+}
+
+pub fn encodable_generic_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
+    let encoder_ty = quote! { __E };
     s.add_impl_generic(parse_quote! { #encoder_ty: ::rustc_serialize::Encoder});
     s.add_bounds(synstructure::AddBounds::Generics);
 
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index a3da8c14f63..bb02a8a1e47 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -196,6 +196,10 @@ impl CStore {
         CrateMetadataRef { cdata, cstore: self }
     }
 
+    pub(crate) fn get_crate_data_mut(&mut self, cnum: CrateNum) -> &mut CrateMetadata {
+        self.metas[cnum].as_mut().unwrap_or_else(|| panic!("Failed to get crate data for {cnum:?}"))
+    }
+
     fn set_crate_data(&mut self, cnum: CrateNum, data: CrateMetadata) {
         assert!(self.metas[cnum].is_none(), "Overwriting crate metadata entry");
         self.metas[cnum] = Some(Box::new(data));
@@ -207,6 +211,12 @@ impl CStore {
             .filter_map(|(cnum, data)| data.as_deref().map(|data| (cnum, data)))
     }
 
+    fn iter_crate_data_mut(&mut self) -> impl Iterator<Item = (CrateNum, &mut CrateMetadata)> {
+        self.metas
+            .iter_enumerated_mut()
+            .filter_map(|(cnum, data)| data.as_deref_mut().map(|data| (cnum, data)))
+    }
+
     fn push_dependencies_in_postorder(&self, deps: &mut Vec<CrateNum>, cnum: CrateNum) {
         if !deps.contains(&cnum) {
             let data = self.get_crate_data(cnum);
@@ -586,11 +596,11 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
 
         match result {
             (LoadResult::Previous(cnum), None) => {
-                let data = self.cstore.get_crate_data(cnum);
+                let data = self.cstore.get_crate_data_mut(cnum);
                 if data.is_proc_macro_crate() {
                     dep_kind = CrateDepKind::MacrosOnly;
                 }
-                data.update_dep_kind(|data_dep_kind| cmp::max(data_dep_kind, dep_kind));
+                data.set_dep_kind(cmp::max(data.dep_kind(), dep_kind));
                 if let Some(private_dep) = private_dep {
                     data.update_and_private_dep(private_dep);
                 }
@@ -637,17 +647,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         }))
     }
 
-    fn update_extern_crate(&self, cnum: CrateNum, extern_crate: ExternCrate) {
-        let cmeta = self.cstore.get_crate_data(cnum);
-        if cmeta.update_extern_crate(extern_crate) {
-            // Propagate the extern crate info to dependencies if it was updated.
-            let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate };
-            for dep_cnum in cmeta.dependencies() {
-                self.update_extern_crate(dep_cnum, extern_crate);
-            }
-        }
-    }
-
     // Go through the crate metadata and load any crates that it references
     fn resolve_crate_deps(
         &mut self,
@@ -726,17 +725,19 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         let mut runtime_found = false;
         let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);
 
+        let mut panic_runtimes = Vec::new();
         for (cnum, data) in self.cstore.iter_crate_data() {
             needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime();
             if data.is_panic_runtime() {
                 // Inject a dependency from all #![needs_panic_runtime] to this
                 // #![panic_runtime] crate.
-                self.inject_dependency_if(cnum, "a panic runtime", &|data| {
-                    data.needs_panic_runtime()
-                });
+                panic_runtimes.push(cnum);
                 runtime_found = runtime_found || data.dep_kind() == CrateDepKind::Explicit;
             }
         }
+        for cnum in panic_runtimes {
+            self.inject_dependency_if(cnum, "a panic runtime", &|data| data.needs_panic_runtime());
+        }
 
         // If an explicitly linked and matching panic runtime was found, or if
         // we just don't need one at all, then we're done here and there's
@@ -917,7 +918,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     }
 
     fn inject_dependency_if(
-        &self,
+        &mut self,
         krate: CrateNum,
         what: &str,
         needs_dep: &dyn Fn(&CrateMetadata) -> bool,
@@ -947,7 +948,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         // crate provided for this compile, but in order for this compilation to
         // be successfully linked we need to inject a dependency (to order the
         // crates on the command line correctly).
-        for (cnum, data) in self.cstore.iter_crate_data() {
+        for (cnum, data) in self.cstore.iter_crate_data_mut() {
             if needs_dep(data) {
                 info!("injecting a dep from {} to {}", cnum, krate);
                 data.add_dependency(krate);
@@ -1031,7 +1032,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
                 let cnum = self.resolve_crate(name, item.span, dep_kind)?;
 
                 let path_len = definitions.def_path(def_id).data.len();
-                self.update_extern_crate(
+                self.cstore.update_extern_crate(
                     cnum,
                     ExternCrate {
                         src: ExternCrateSource::Extern(def_id.to_def_id()),
@@ -1049,7 +1050,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     pub fn process_path_extern(&mut self, name: Symbol, span: Span) -> Option<CrateNum> {
         let cnum = self.resolve_crate(name, span, CrateDepKind::Explicit)?;
 
-        self.update_extern_crate(
+        self.cstore.update_extern_crate(
             cnum,
             ExternCrate {
                 src: ExternCrateSource::Path,
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index e13068cb6f9..27c26d31781 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -500,10 +500,10 @@ pub(crate) struct MultipleCandidates {
 impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for MultipleCandidates {
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_multiple_candidates);
-        diag.set_arg("crate_name", self.crate_name);
-        diag.set_arg("flavor", self.flavor);
+        diag.arg("crate_name", self.crate_name);
+        diag.arg("flavor", self.flavor);
         diag.code(error_code!(E0464));
-        diag.set_span(self.span);
+        diag.span(self.span);
         for (i, candidate) in self.candidates.iter().enumerate() {
             diag.note(format!("candidate #{}: {}", i + 1, candidate.display()));
         }
@@ -596,10 +596,10 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidMetadataFiles {
     #[track_caller]
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_invalid_meta_files);
-        diag.set_arg("crate_name", self.crate_name);
-        diag.set_arg("add_info", self.add_info);
+        diag.arg("crate_name", self.crate_name);
+        diag.arg("add_info", self.add_info);
         diag.code(error_code!(E0786));
-        diag.set_span(self.span);
+        diag.span(self.span);
         for crate_rejection in self.crate_rejections {
             diag.note(crate_rejection);
         }
@@ -623,12 +623,12 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for CannotFindCrate {
     #[track_caller]
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::metadata_cannot_find_crate);
-        diag.set_arg("crate_name", self.crate_name);
-        diag.set_arg("current_crate", self.current_crate);
-        diag.set_arg("add_info", self.add_info);
-        diag.set_arg("locator_triple", self.locator_triple.triple());
+        diag.arg("crate_name", self.crate_name);
+        diag.arg("current_crate", self.current_crate);
+        diag.arg("add_info", self.add_info);
+        diag.arg("locator_triple", self.locator_triple.triple());
         diag.code(error_code!(E0463));
-        diag.set_span(self.span);
+        diag.span(self.span);
         if (self.crate_name == sym::std || self.crate_name == sym::core)
             && self.locator_triple != TargetTriple::from_triple(config::host_triple())
         {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 2de29db9e5c..49e849964be 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -8,7 +8,7 @@ use rustc_ast as ast;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::owned_slice::OwnedSlice;
-use rustc_data_structures::sync::{AppendOnlyVec, AtomicBool, Lock, Lrc, OnceLock};
+use rustc_data_structures::sync::{Lock, Lrc, OnceLock};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
 use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro};
@@ -26,12 +26,11 @@ use rustc_serialize::{Decodable, Decoder};
 use rustc_session::cstore::{CrateSource, ExternCrate};
 use rustc_session::Session;
 use rustc_span::symbol::kw;
-use rustc_span::{BytePos, Pos, SpanData, SyntaxContext, DUMMY_SP};
+use rustc_span::{BytePos, Pos, SpanData, SpanDecoder, SyntaxContext, DUMMY_SP};
 
 use proc_macro::bridge::client::ProcMacro;
 use std::iter::TrustedLen;
 use std::path::Path;
-use std::sync::atomic::Ordering;
 use std::{io, iter, mem};
 
 pub(super) use cstore_impl::provide;
@@ -96,15 +95,15 @@ pub(crate) struct CrateMetadata {
     /// IDs as they are seen from the current compilation session.
     cnum_map: CrateNumMap,
     /// Same ID set as `cnum_map` plus maybe some injected crates like panic runtime.
-    dependencies: AppendOnlyVec<CrateNum>,
+    dependencies: Vec<CrateNum>,
     /// How to link (or not link) this crate to the currently compiled crate.
-    dep_kind: Lock<CrateDepKind>,
+    dep_kind: CrateDepKind,
     /// Filesystem location of this crate.
     source: Lrc<CrateSource>,
     /// Whether or not this crate should be consider a private dependency.
     /// Used by the 'exported_private_dependencies' lint, and for determining
     /// whether to emit suggestions that reference this crate.
-    private_dep: AtomicBool,
+    private_dep: bool,
     /// The hash for the host proc macro. Used to support `-Z dual-proc-macro`.
     host_hash: Option<Svh>,
 
@@ -118,7 +117,7 @@ pub(crate) struct CrateMetadata {
     // --- Data used only for improving diagnostics ---
     /// Information about the `extern crate` item or path that caused this crate to be loaded.
     /// If this is `None`, then the crate was injected (e.g., by the allocator).
-    extern_crate: Lock<Option<ExternCrate>>,
+    extern_crate: Option<ExternCrate>,
 }
 
 /// Holds information about a rustc_span::SourceFile imported from another crate.
@@ -409,21 +408,6 @@ impl<'a, 'tcx> TyDecoder for DecodeContext<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for CrateNum {
-    #[inline]
-    fn decode(d: &mut DecodeContext<'a, 'tcx>) -> CrateNum {
-        let cnum = CrateNum::from_u32(d.read_u32());
-        d.map_encoded_cnum_to_current(cnum)
-    }
-}
-
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for DefIndex {
-    #[inline]
-    fn decode(d: &mut DecodeContext<'a, 'tcx>) -> DefIndex {
-        DefIndex::from_u32(d.read_u32())
-    }
-}
-
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnIndex {
     #[inline]
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> ExpnIndex {
@@ -431,19 +415,29 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnIndex {
     }
 }
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ast::AttrId {
-    #[inline]
-    fn decode(d: &mut DecodeContext<'a, 'tcx>) -> ast::AttrId {
-        let sess = d.sess.expect("can't decode AttrId without Session");
+impl<'a, 'tcx> SpanDecoder for DecodeContext<'a, 'tcx> {
+    fn decode_attr_id(&mut self) -> rustc_span::AttrId {
+        let sess = self.sess.expect("can't decode AttrId without Session");
         sess.parse_sess.attr_id_generator.mk_attr_id()
     }
-}
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
-    fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> SyntaxContext {
-        let cdata = decoder.cdata();
+    fn decode_crate_num(&mut self) -> CrateNum {
+        let cnum = CrateNum::from_u32(self.read_u32());
+        self.map_encoded_cnum_to_current(cnum)
+    }
 
-        let Some(sess) = decoder.sess else {
+    fn decode_def_index(&mut self) -> DefIndex {
+        DefIndex::from_u32(self.read_u32())
+    }
+
+    fn decode_def_id(&mut self) -> DefId {
+        DefId { krate: Decodable::decode(self), index: Decodable::decode(self) }
+    }
+
+    fn decode_syntax_context(&mut self) -> SyntaxContext {
+        let cdata = self.cdata();
+
+        let Some(sess) = self.sess else {
             bug!(
                 "Cannot decode SyntaxContext without Session.\
                 You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`."
@@ -451,7 +445,7 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
         };
 
         let cname = cdata.root.name();
-        rustc_span::hygiene::decode_syntax_context(decoder, &cdata.hygiene_context, |_, id| {
+        rustc_span::hygiene::decode_syntax_context(self, &cdata.hygiene_context, |_, id| {
             debug!("SpecializedDecoder<SyntaxContext>: decoding {}", id);
             cdata
                 .root
@@ -461,21 +455,19 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SyntaxContext {
                 .decode((cdata, sess))
         })
     }
-}
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId {
-    fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> ExpnId {
-        let local_cdata = decoder.cdata();
+    fn decode_expn_id(&mut self) -> ExpnId {
+        let local_cdata = self.cdata();
 
-        let Some(sess) = decoder.sess else {
+        let Some(sess) = self.sess else {
             bug!(
                 "Cannot decode ExpnId without Session. \
                 You need to explicitly pass `(crate_metadata_ref, tcx)` to `decode` instead of just `crate_metadata_ref`."
             );
         };
 
-        let cnum = CrateNum::decode(decoder);
-        let index = u32::decode(decoder);
+        let cnum = CrateNum::decode(self);
+        let index = u32::decode(self);
 
         let expn_id = rustc_span::hygiene::decode_expn_id(cnum, index, |expn_id| {
             let ExpnId { krate: cnum, local_id: index } = expn_id;
@@ -503,27 +495,51 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for ExpnId {
         });
         expn_id
     }
-}
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Span {
-    fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Span {
-        let start = decoder.position();
-        let tag = SpanTag(decoder.peek_byte());
+    fn decode_span(&mut self) -> Span {
+        let start = self.position();
+        let tag = SpanTag(self.peek_byte());
         let data = if tag.kind() == SpanKind::Indirect {
             // Skip past the tag we just peek'd.
-            decoder.read_u8();
-            let offset_or_position = decoder.read_usize();
+            self.read_u8();
+            let offset_or_position = self.read_usize();
             let position = if tag.is_relative_offset() {
                 start - offset_or_position
             } else {
                 offset_or_position
             };
-            decoder.with_position(position, SpanData::decode)
+            self.with_position(position, SpanData::decode)
         } else {
-            SpanData::decode(decoder)
+            SpanData::decode(self)
         };
         Span::new(data.lo, data.hi, data.ctxt, data.parent)
     }
+
+    fn decode_symbol(&mut self) -> Symbol {
+        let tag = self.read_u8();
+
+        match tag {
+            SYMBOL_STR => {
+                let s = self.read_str();
+                Symbol::intern(s)
+            }
+            SYMBOL_OFFSET => {
+                // read str offset
+                let pos = self.read_usize();
+
+                // move to str offset and read
+                self.opaque.with_position(pos, |d| {
+                    let s = d.read_str();
+                    Symbol::intern(s)
+                })
+            }
+            SYMBOL_PREINTERNED => {
+                let symbol_index = self.read_u32();
+                Symbol::new_from_decoded(symbol_index)
+            }
+            _ => unreachable!(),
+        }
+    }
 }
 
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SpanData {
@@ -631,34 +647,6 @@ impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for SpanData {
     }
 }
 
-impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for Symbol {
-    fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
-        let tag = d.read_u8();
-
-        match tag {
-            SYMBOL_STR => {
-                let s = d.read_str();
-                Symbol::intern(s)
-            }
-            SYMBOL_OFFSET => {
-                // read str offset
-                let pos = d.read_usize();
-
-                // move to str offset and read
-                d.opaque.with_position(pos, |d| {
-                    let s = d.read_str();
-                    Symbol::intern(s)
-                })
-            }
-            SYMBOL_PREINTERNED => {
-                let symbol_index = d.read_u32();
-                Symbol::new_from_decoded(symbol_index)
-            }
-            _ => unreachable!(),
-        }
-    }
-}
-
 impl<'a, 'tcx> Decodable<DecodeContext<'a, 'tcx>> for &'tcx [(ty::Clause<'tcx>, Span)] {
     fn decode(d: &mut DecodeContext<'a, 'tcx>) -> Self {
         ty::codec::RefDecodable::decode(d)
@@ -1818,11 +1806,11 @@ impl CrateMetadata {
             cnum,
             cnum_map,
             dependencies,
-            dep_kind: Lock::new(dep_kind),
+            dep_kind,
             source: Lrc::new(source),
-            private_dep: AtomicBool::new(private_dep),
+            private_dep,
             host_hash,
-            extern_crate: Lock::new(None),
+            extern_crate: None,
             hygiene_context: Default::default(),
             def_key_cache: Default::default(),
         };
@@ -1839,18 +1827,18 @@ impl CrateMetadata {
     }
 
     pub(crate) fn dependencies(&self) -> impl Iterator<Item = CrateNum> + '_ {
-        self.dependencies.iter()
+        self.dependencies.iter().copied()
     }
 
-    pub(crate) fn add_dependency(&self, cnum: CrateNum) {
+    pub(crate) fn add_dependency(&mut self, cnum: CrateNum) {
         self.dependencies.push(cnum);
     }
 
-    pub(crate) fn update_extern_crate(&self, new_extern_crate: ExternCrate) -> bool {
-        let mut extern_crate = self.extern_crate.borrow_mut();
-        let update = Some(new_extern_crate.rank()) > extern_crate.as_ref().map(ExternCrate::rank);
+    pub(crate) fn update_extern_crate(&mut self, new_extern_crate: ExternCrate) -> bool {
+        let update =
+            Some(new_extern_crate.rank()) > self.extern_crate.as_ref().map(ExternCrate::rank);
         if update {
-            *extern_crate = Some(new_extern_crate);
+            self.extern_crate = Some(new_extern_crate);
         }
         update
     }
@@ -1860,15 +1848,15 @@ impl CrateMetadata {
     }
 
     pub(crate) fn dep_kind(&self) -> CrateDepKind {
-        *self.dep_kind.lock()
+        self.dep_kind
     }
 
-    pub(crate) fn update_dep_kind(&self, f: impl FnOnce(CrateDepKind) -> CrateDepKind) {
-        self.dep_kind.with_lock(|dep_kind| *dep_kind = f(*dep_kind))
+    pub(crate) fn set_dep_kind(&mut self, dep_kind: CrateDepKind) {
+        self.dep_kind = dep_kind;
     }
 
-    pub(crate) fn update_and_private_dep(&self, private_dep: bool) {
-        self.private_dep.fetch_and(private_dep, Ordering::SeqCst);
+    pub(crate) fn update_and_private_dep(&mut self, private_dep: bool) {
+        self.private_dep &= private_dep;
     }
 
     pub(crate) fn required_panic_strategy(&self) -> Option<PanicStrategy> {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index bb8f4af8e97..912c2f36eb3 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -19,7 +19,7 @@ use rustc_middle::query::LocalCrate;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_middle::util::Providers;
-use rustc_session::cstore::CrateStore;
+use rustc_session::cstore::{CrateStore, ExternCrate};
 use rustc_session::{Session, StableCrateId};
 use rustc_span::hygiene::{ExpnHash, ExpnId};
 use rustc_span::symbol::{kw, Symbol};
@@ -290,13 +290,7 @@ provide! { tcx, def_id, other, cdata,
     cross_crate_inlinable => { cdata.cross_crate_inlinable(def_id.index) }
 
     dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
-    is_private_dep => {
-        // Parallel compiler needs to synchronize type checking and linting (which use this flag)
-        // so that they happen strictly crate loading. Otherwise, the full list of available
-        // impls aren't loaded yet.
-        use std::sync::atomic::Ordering;
-        cdata.private_dep.load(Ordering::Acquire)
-    }
+    is_private_dep => { cdata.private_dep }
     is_panic_runtime => { cdata.root.panic_runtime }
     is_compiler_builtins => { cdata.root.compiler_builtins }
     has_global_allocator => { cdata.root.has_global_allocator }
@@ -305,10 +299,7 @@ provide! { tcx, def_id, other, cdata,
     is_profiler_runtime => { cdata.root.profiler_runtime }
     required_panic_strategy => { cdata.root.required_panic_strategy }
     panic_in_drop_strategy => { cdata.root.panic_in_drop_strategy }
-    extern_crate => {
-        let r = *cdata.extern_crate.lock();
-        r.map(|c| &*tcx.arena.alloc(c))
-    }
+    extern_crate => { cdata.extern_crate.map(|c| &*tcx.arena.alloc(c)) }
     is_no_builtins => { cdata.root.no_builtins }
     symbol_mangling_version => { cdata.root.symbol_mangling_version }
     reachable_non_generics => {
@@ -339,10 +330,7 @@ provide! { tcx, def_id, other, cdata,
     implementations_of_trait => { cdata.get_implementations_of_trait(tcx, other) }
     crate_incoherent_impls => { cdata.get_incoherent_impls(tcx, other) }
 
-    dep_kind => {
-        let r = *cdata.dep_kind.lock();
-        r
-    }
+    dep_kind => { cdata.dep_kind }
     module_children => {
         tcx.arena.alloc_from_iter(cdata.get_module_children(def_id.index, tcx.sess))
     }
@@ -357,8 +345,7 @@ provide! { tcx, def_id, other, cdata,
     missing_lang_items => { cdata.get_missing_lang_items(tcx) }
 
     missing_extern_crate_item => {
-        let r = matches!(*cdata.extern_crate.borrow(), Some(extern_crate) if !extern_crate.is_direct());
-        r
+        matches!(cdata.extern_crate, Some(extern_crate) if !extern_crate.is_direct())
     }
 
     used_crate_source => { Lrc::clone(&cdata.source) }
@@ -581,6 +568,19 @@ impl CStore {
     ) -> Span {
         self.get_crate_data(cnum).get_proc_macro_quoted_span(id, sess)
     }
+
+    pub(crate) fn update_extern_crate(&mut self, cnum: CrateNum, extern_crate: ExternCrate) {
+        let cmeta = self.get_crate_data_mut(cnum);
+        if cmeta.update_extern_crate(extern_crate) {
+            // Propagate the extern crate info to dependencies if it was updated.
+            let extern_crate = ExternCrate { dependency_of: cnum, ..extern_crate };
+            let dependencies = std::mem::take(&mut cmeta.dependencies);
+            for &dep_cnum in &dependencies {
+                self.update_extern_crate(dep_cnum, extern_crate);
+            }
+            self.get_crate_data_mut(cnum).dependencies = dependencies;
+        }
+    }
 }
 
 impl CrateStore for CStore {
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 5b296c098bc..a458b528a97 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2,10 +2,8 @@ use crate::errors::{FailCreateFileEncoder, FailWriteFile};
 use crate::rmeta::*;
 
 use rustc_ast::Attribute;
-use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::memmap::{Mmap, MmapMut};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::{join, par_for_each_in, Lrc};
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_hir as hir;
@@ -27,7 +25,7 @@ use rustc_session::config::{CrateType, OptLevel};
 use rustc_span::hygiene::HygieneEncodeContext;
 use rustc_span::symbol::sym;
 use rustc_span::{
-    ExternalSource, FileName, SourceFile, SpanData, StableSourceFileId, SyntaxContext,
+    ExternalSource, FileName, SourceFile, SpanData, SpanEncoder, StableSourceFileId, SyntaxContext,
 };
 use std::borrow::Borrow;
 use std::collections::hash_map::Entry;
@@ -125,70 +123,90 @@ impl<'a, 'tcx, I, T> Encodable<EncodeContext<'a, 'tcx>> for LazyTable<I, T> {
     }
 }
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for CrateNum {
+impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
     fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        if *self != LOCAL_CRATE && s.is_proc_macro {
-            panic!("Attempted to encode non-local CrateNum {self:?} for proc-macro crate");
-        }
         s.emit_u32(self.as_u32());
     }
 }
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for DefIndex {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        s.emit_u32(self.as_u32());
+impl<'a, 'tcx> SpanEncoder for EncodeContext<'a, 'tcx> {
+    fn encode_crate_num(&mut self, crate_num: CrateNum) {
+        if crate_num != LOCAL_CRATE && self.is_proc_macro {
+            panic!("Attempted to encode non-local CrateNum {crate_num:?} for proc-macro crate");
+        }
+        self.emit_u32(crate_num.as_u32());
     }
-}
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnIndex {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        s.emit_u32(self.as_u32());
+    fn encode_def_index(&mut self, def_index: DefIndex) {
+        self.emit_u32(def_index.as_u32());
     }
-}
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for SyntaxContext {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_ctxt, s);
+    fn encode_def_id(&mut self, def_id: DefId) {
+        def_id.krate.encode(self);
+        def_id.index.encode(self);
     }
-}
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for ExpnId {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        if self.krate == LOCAL_CRATE {
+    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
+        rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_ctxt, self);
+    }
+
+    fn encode_expn_id(&mut self, expn_id: ExpnId) {
+        if expn_id.krate == LOCAL_CRATE {
             // We will only write details for local expansions. Non-local expansions will fetch
             // data from the corresponding crate's metadata.
             // FIXME(#43047) FIXME(#74731) We may eventually want to avoid relying on external
             // metadata from proc-macro crates.
-            s.hygiene_ctxt.schedule_expn_data_for_encoding(*self);
+            self.hygiene_ctxt.schedule_expn_data_for_encoding(expn_id);
         }
-        self.krate.encode(s);
-        self.local_id.encode(s);
+        expn_id.krate.encode(self);
+        expn_id.local_id.encode(self);
     }
-}
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Span {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        match s.span_shorthands.entry(*self) {
+    fn encode_span(&mut self, span: Span) {
+        match self.span_shorthands.entry(span) {
             Entry::Occupied(o) => {
                 // If an offset is smaller than the absolute position, we encode with the offset.
                 // This saves space since smaller numbers encode in less bits.
                 let last_location = *o.get();
                 // This cannot underflow. Metadata is written with increasing position(), so any
                 // previously saved offset must be smaller than the current position.
-                let offset = s.opaque.position() - last_location;
+                let offset = self.opaque.position() - last_location;
                 if offset < last_location {
-                    SpanTag::indirect(true).encode(s);
-                    offset.encode(s);
+                    SpanTag::indirect(true).encode(self);
+                    offset.encode(self);
                 } else {
-                    SpanTag::indirect(false).encode(s);
-                    last_location.encode(s);
+                    SpanTag::indirect(false).encode(self);
+                    last_location.encode(self);
                 }
             }
             Entry::Vacant(v) => {
-                let position = s.opaque.position();
+                let position = self.opaque.position();
                 v.insert(position);
                 // Data is encoded with a SpanTag prefix (see below).
-                self.data().encode(s);
+                span.data().encode(self);
+            }
+        }
+    }
+
+    fn encode_symbol(&mut self, symbol: Symbol) {
+        // if symbol preinterned, emit tag and symbol index
+        if symbol.is_preinterned() {
+            self.opaque.emit_u8(SYMBOL_PREINTERNED);
+            self.opaque.emit_u32(symbol.as_u32());
+        } else {
+            // otherwise write it as string or as offset to it
+            match self.symbol_table.entry(symbol) {
+                Entry::Vacant(o) => {
+                    self.opaque.emit_u8(SYMBOL_STR);
+                    let pos = self.opaque.position();
+                    o.insert(pos);
+                    self.emit_str(symbol.as_str());
+                }
+                Entry::Occupied(o) => {
+                    let x = *o.get();
+                    self.emit_u8(SYMBOL_OFFSET);
+                    self.emit_usize(x);
+                }
             }
         }
     }
@@ -337,31 +355,6 @@ impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for SpanData {
     }
 }
 
-impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for Symbol {
-    fn encode(&self, s: &mut EncodeContext<'a, 'tcx>) {
-        // if symbol preinterned, emit tag and symbol index
-        if self.is_preinterned() {
-            s.opaque.emit_u8(SYMBOL_PREINTERNED);
-            s.opaque.emit_u32(self.as_u32());
-        } else {
-            // otherwise write it as string or as offset to it
-            match s.symbol_table.entry(*self) {
-                Entry::Vacant(o) => {
-                    s.opaque.emit_u8(SYMBOL_STR);
-                    let pos = s.opaque.position();
-                    o.insert(pos);
-                    s.emit_str(self.as_str());
-                }
-                Entry::Occupied(o) => {
-                    let x = *o.get();
-                    s.emit_u8(SYMBOL_OFFSET);
-                    s.emit_usize(x);
-                }
-            }
-        }
-    }
-}
-
 impl<'a, 'tcx> Encodable<EncodeContext<'a, 'tcx>> for [u8] {
     fn encode(&self, e: &mut EncodeContext<'a, 'tcx>) {
         Encoder::emit_usize(e, self.len());
@@ -1914,14 +1907,15 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         empty_proc_macro!(self);
         let tcx = self.tcx;
         let lib_features = tcx.lib_features(LOCAL_CRATE);
-        self.lazy_array(lib_features.to_vec())
+        self.lazy_array(lib_features.to_sorted_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)))
+        let sorted = implications.to_sorted_stable_ord();
+        self.lazy_array(sorted.into_iter().map(|(k, v)| (*k, *v)))
     }
 
     fn encode_diagnostic_items(&mut self) -> LazyArray<(Symbol, DefIndex)> {
@@ -2033,14 +2027,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
     fn encode_incoherent_impls(&mut self) -> LazyArray<IncoherentImpls> {
         empty_proc_macro!(self);
         let tcx = self.tcx;
-        let mut all_impls: Vec<_> = tcx.crate_inherent_impls(()).incoherent_impls.iter().collect();
-        tcx.with_stable_hashing_context(|mut ctx| {
-            all_impls.sort_by_cached_key(|&(&simp, _)| {
-                let mut hasher = StableHasher::new();
-                simp.hash_stable(&mut ctx, &mut hasher);
-                hasher.finish::<Fingerprint>()
-            })
+        let all_impls = tcx.with_stable_hashing_context(|hcx| {
+            tcx.crate_inherent_impls(()).incoherent_impls.to_sorted(&hcx, true)
         });
+
         let all_impls: Vec<_> = all_impls
             .into_iter()
             .map(|(&simp, impls)| {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 54ee50c2358..2f775882693 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -12,7 +12,7 @@ use rustc_attr as attr;
 use rustc_data_structures::svh::Svh;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap};
-use rustc_hir::def_id::{CrateNum, DefId, DefIndex, DefPathHash, StableCrateId};
+use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId};
 use rustc_hir::definitions::DefKey;
 use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::BitSet;
@@ -459,7 +459,7 @@ define_tables! {
     macro_definition: Table<DefIndex, LazyValue<ast::DelimArgs>>,
     proc_macro: Table<DefIndex, MacroKind>,
     deduced_param_attrs: Table<DefIndex, LazyArray<DeducedParamAttrs>>,
-    trait_impl_trait_tys: Table<DefIndex, LazyValue<FxHashMap<DefId, ty::EarlyBinder<Ty<'static>>>>>,
+    trait_impl_trait_tys: Table<DefIndex, LazyValue<DefIdMap<ty::EarlyBinder<Ty<'static>>>>>,
     doc_link_resolutions: Table<DefIndex, LazyValue<DocLinkResMap>>,
     doc_link_traits_in_scope: Table<DefIndex, LazyArray<DefId>>,
     assumed_wf_types_for_rpitit: Table<DefIndex, LazyArray<(Ty<'static>, Span)>>,
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 52fd494a10d..0ab09dadf58 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -103,7 +103,7 @@ macro_rules! arena_types {
             [] dep_kind: rustc_middle::dep_graph::DepKindStruct<'tcx>,
 
             [decode] trait_impl_trait_tys:
-                rustc_data_structures::fx::FxHashMap<
+                rustc_data_structures::unord::UnordMap<
                     rustc_hir::def_id::DefId,
                     rustc_middle::ty::EarlyBinder<rustc_middle::ty::Ty<'tcx>>
                 >,
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index ae432a04065..c5e4dfaf19e 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -312,16 +312,17 @@ pub fn struct_lint_level(
                 // create a `DiagnosticBuilder` and continue as we would for warnings.
                 rustc_errors::Level::Expect(expect_id)
             }
-            Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::Warning(Some(expect_id)),
-            Level::Warn | Level::ForceWarn(None) => rustc_errors::Level::Warning(None),
-            Level::Deny | Level::Forbid => rustc_errors::Level::Error { lint: true },
+            Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)),
+            Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None),
+            Level::Warn => rustc_errors::Level::Warning,
+            Level::Deny | Level::Forbid => rustc_errors::Level::Error,
         };
         let mut err = DiagnosticBuilder::new(sess.dcx(), err_level, "");
         if let Some(span) = span {
-            err.set_span(span);
+            err.span(span);
         }
 
-        err.set_is_lint();
+        err.is_lint();
 
         // If this code originates in a foreign macro, aka something that this crate
         // did not itself author, then it's likely that there's nothing this crate
@@ -348,25 +349,21 @@ pub fn struct_lint_level(
 
         // Delay evaluating and setting the primary message until after we've
         // suppressed the lint due to macros.
-        err.set_primary_message(msg);
+        err.primary_message(msg);
+
+        let name = lint.name_lower();
+        err.code(DiagnosticId::Lint { name, has_future_breakage });
 
         // Lint diagnostics that are covered by the expect level will not be emitted outside
         // the compiler. It is therefore not necessary to add any information for the user.
         // This will therefore directly call the decorate function which will in turn emit
         // the `Diagnostic`.
         if let Level::Expect(_) = level {
-            let name = lint.name_lower();
-            err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false });
-
             decorate(&mut err);
             err.emit();
             return;
         }
 
-        let name = lint.name_lower();
-        let is_force_warn = matches!(level, Level::ForceWarn(_));
-        err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn });
-
         if let Some(future_incompatible) = future_incompatible {
             let explanation = match future_incompatible.reason {
                 FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps
diff --git a/compiler/rustc_middle/src/middle/mod.rs b/compiler/rustc_middle/src/middle/mod.rs
index 8c1b1ff12e9..bdb2270611a 100644
--- a/compiler/rustc_middle/src/middle/mod.rs
+++ b/compiler/rustc_middle/src/middle/mod.rs
@@ -4,7 +4,7 @@ pub mod dependency_format;
 pub mod exported_symbols;
 pub mod lang_items;
 pub mod lib_features {
-    use rustc_data_structures::fx::FxHashMap;
+    use rustc_data_structures::unord::UnordMap;
     use rustc_span::{symbol::Symbol, Span};
 
     #[derive(Copy, Clone, Debug, PartialEq, Eq)]
@@ -16,15 +16,16 @@ pub mod lib_features {
 
     #[derive(HashStable, Debug, Default)]
     pub struct LibFeatures {
-        pub stability: FxHashMap<Symbol, (FeatureStability, Span)>,
+        pub stability: UnordMap<Symbol, (FeatureStability, Span)>,
     }
 
     impl LibFeatures {
-        pub fn to_vec(&self) -> Vec<(Symbol, FeatureStability)> {
-            let mut all_features: Vec<_> =
-                self.stability.iter().map(|(&sym, &(stab, _))| (sym, stab)).collect();
-            all_features.sort_unstable_by(|(a, _), (b, _)| a.as_str().cmp(b.as_str()));
-            all_features
+        pub fn to_sorted_vec(&self) -> Vec<(Symbol, FeatureStability)> {
+            self.stability
+                .to_sorted_stable_ord()
+                .iter()
+                .map(|(&sym, &(stab, _))| (sym, stab))
+                .collect()
         }
     }
 }
diff --git a/compiler/rustc_middle/src/middle/region.rs b/compiler/rustc_middle/src/middle/region.rs
index b4dd8f6f4a7..5d6a7f75df8 100644
--- a/compiler/rustc_middle/src/middle/region.rs
+++ b/compiler/rustc_middle/src/middle/region.rs
@@ -7,12 +7,11 @@
 //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
 
 use crate::ty::TyCtxt;
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::UnordMap;
 use rustc_hir as hir;
 use rustc_hir::{HirIdMap, Node};
 use rustc_macros::HashStable;
-use rustc_query_system::ich::StableHashingContext;
 use rustc_span::{Span, DUMMY_SP};
 
 use std::fmt;
@@ -205,7 +204,7 @@ impl Scope {
 pub type ScopeDepth = u32;
 
 /// The region scope tree encodes information about region relationships.
-#[derive(Default, Debug)]
+#[derive(Default, Debug, HashStable)]
 pub struct ScopeTree {
     /// If not empty, this body is the root of this region hierarchy.
     pub root_body: Option<hir::HirId>,
@@ -306,7 +305,7 @@ pub struct ScopeTree {
     /// The reason is that semantically, until the `box` expression returns,
     /// the values are still owned by their containing expressions. So
     /// we'll see that `&x`.
-    pub yield_in_scope: FxHashMap<Scope, Vec<YieldData>>,
+    pub yield_in_scope: UnordMap<Scope, Vec<YieldData>>,
 }
 
 /// Identifies the reason that a given expression is an rvalue candidate
@@ -404,23 +403,3 @@ impl ScopeTree {
         self.yield_in_scope.get(&scope).map(Deref::deref)
     }
 }
-
-impl<'a> HashStable<StableHashingContext<'a>> for ScopeTree {
-    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
-        let ScopeTree {
-            root_body,
-            ref parent_map,
-            ref var_map,
-            ref destruction_scopes,
-            ref rvalue_candidates,
-            ref yield_in_scope,
-        } = *self;
-
-        root_body.hash_stable(hcx, hasher);
-        parent_map.hash_stable(hcx, hasher);
-        var_map.hash_stable(hcx, hasher);
-        destruction_scopes.hash_stable(hcx, hasher);
-        rvalue_candidates.hash_stable(hcx, hasher);
-        yield_in_scope.hash_stable(hcx, hasher);
-    }
-}
diff --git a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
index c59704fc023..610afd95f3c 100644
--- a/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
+++ b/compiler/rustc_middle/src/middle/resolve_bound_vars.rs
@@ -2,7 +2,7 @@
 
 use crate::ty;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{ItemLocalId, OwnerId};
@@ -51,7 +51,7 @@ pub enum ObjectLifetimeDefault {
 pub struct ResolveBoundVars {
     /// Maps from every use of a named (not anonymous) lifetime to a
     /// `Region` describing how that region is bound
-    pub defs: FxHashMap<OwnerId, FxHashMap<ItemLocalId, ResolvedArg>>,
+    pub defs: FxIndexMap<OwnerId, FxIndexMap<ItemLocalId, ResolvedArg>>,
 
-    pub late_bound_vars: FxHashMap<OwnerId, FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>>,
+    pub late_bound_vars: FxIndexMap<OwnerId, FxIndexMap<ItemLocalId, Vec<ty::BoundVariableKind>>>,
 }
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index b2d1124b2ed..90b479cf2f4 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -8,11 +8,11 @@ use rustc_ast::NodeId;
 use rustc_attr::{
     self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability,
 };
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_feature::GateIssue;
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
 use rustc_hir::{self as hir, HirId};
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE, SOFT_UNSTABLE};
@@ -61,10 +61,10 @@ impl DeprecationEntry {
 pub struct Index {
     /// This is mostly a cache, except the stabilities of local items
     /// are filled by the annotator.
-    pub stab_map: FxHashMap<LocalDefId, Stability>,
-    pub const_stab_map: FxHashMap<LocalDefId, ConstStability>,
-    pub default_body_stab_map: FxHashMap<LocalDefId, DefaultBodyStability>,
-    pub depr_map: FxHashMap<LocalDefId, DeprecationEntry>,
+    pub stab_map: LocalDefIdMap<Stability>,
+    pub const_stab_map: LocalDefIdMap<ConstStability>,
+    pub default_body_stab_map: LocalDefIdMap<DefaultBodyStability>,
+    pub depr_map: LocalDefIdMap<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.
@@ -77,7 +77,7 @@ pub struct Index {
     /// 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>,
+    pub implications: UnordMap<Symbol, Symbol>,
 }
 
 impl Index {
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index a92b85a716f..4047891d769 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -14,7 +14,6 @@ use either::{Left, Right};
 
 use rustc_ast::Mutability;
 use rustc_data_structures::intern::Interned;
-use rustc_span::DUMMY_SP;
 use rustc_target::abi::{Align, HasDataLayout, Size};
 
 use super::{
@@ -314,9 +313,7 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
     /// available to the compiler to do so.
     pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> {
         Self::uninit_inner(size, align, || {
-            ty::tls::with(|tcx| {
-                tcx.dcx().span_delayed_bug(DUMMY_SP, "exhausted memory during interpretation")
-            });
+            ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation"));
             InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted).into()
         })
     }
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index e6536074f35..1b4e9c28635 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -2,14 +2,10 @@ use super::{AllocId, AllocRange, Pointer, Scalar};
 
 use crate::error;
 use crate::mir::{ConstAlloc, ConstValue};
-use crate::query::TyCtxtAt;
 use crate::ty::{layout, tls, Ty, TyCtxt, ValTree};
 
 use rustc_data_structures::sync::Lock;
-use rustc_errors::{
-    struct_span_err, DiagnosticArgValue, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
-    IntoDiagnosticArg,
-};
+use rustc_errors::{DiagnosticArgValue, DiagnosticMessage, ErrorGuaranteed, IntoDiagnosticArg};
 use rustc_macros::HashStable;
 use rustc_session::CtfeBacktrace;
 use rustc_span::{def_id::DefId, Span, DUMMY_SP};
@@ -90,10 +86,6 @@ pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
 /// This is needed in `thir::pattern::lower_inline_const`.
 pub type EvalToValTreeResult<'tcx> = Result<Option<ValTree<'tcx>>, ErrorHandled>;
 
-pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
-    struct_span_err!(tcx.dcx(), tcx.span, E0080, "{}", msg)
-}
-
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 static_assert_size!(InterpErrorInfo<'_>, 8);
 
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 2db56008553..0da3524e055 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -142,12 +142,11 @@ use crate::ty::GenericArgKind;
 use crate::ty::{self, Instance, Ty, TyCtxt};
 
 pub use self::error::{
-    struct_error, BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled,
-    EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, ExpectedKind,
-    InterpError, InterpErrorInfo, InterpResult, InvalidMetaKind, InvalidProgramInfo,
-    MachineStopType, Misalignment, PointerKind, ReportedErrorInfo, ResourceExhaustionInfo,
-    ScalarSizeMismatch, UndefinedBehaviorInfo, UnsupportedOpInfo, ValidationErrorInfo,
-    ValidationErrorKind,
+    BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalToAllocationRawResult,
+    EvalToConstValueResult, EvalToValTreeResult, ExpectedKind, InterpError, InterpErrorInfo,
+    InterpResult, InvalidMetaKind, InvalidProgramInfo, MachineStopType, Misalignment, PointerKind,
+    ReportedErrorInfo, ResourceExhaustionInfo, ScalarSizeMismatch, UndefinedBehaviorInfo,
+    UnsupportedOpInfo, ValidationErrorInfo, ValidationErrorKind,
 };
 
 pub use self::value::Scalar;
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 5c425fef27e..36f5ba161d5 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -55,7 +55,6 @@ pub mod mono;
 pub mod patch;
 pub mod pretty;
 mod query;
-pub mod spanview;
 mod statement;
 mod syntax;
 pub mod tcx;
@@ -250,6 +249,9 @@ pub struct CoroutineInfo<'tcx> {
     /// The yield type of the function, if it is a coroutine.
     pub yield_ty: Option<Ty<'tcx>>,
 
+    /// The resume type of the function, if it is a coroutine.
+    pub resume_ty: Option<Ty<'tcx>>,
+
     /// Coroutine drop glue.
     pub coroutine_drop: Option<Body<'tcx>>,
 
@@ -261,6 +263,23 @@ pub struct CoroutineInfo<'tcx> {
     pub coroutine_kind: CoroutineKind,
 }
 
+impl<'tcx> CoroutineInfo<'tcx> {
+    // Sets up `CoroutineInfo` for a pre-coroutine-transform MIR body.
+    pub fn initial(
+        coroutine_kind: CoroutineKind,
+        yield_ty: Ty<'tcx>,
+        resume_ty: Ty<'tcx>,
+    ) -> CoroutineInfo<'tcx> {
+        CoroutineInfo {
+            coroutine_kind,
+            yield_ty: Some(yield_ty),
+            resume_ty: Some(resume_ty),
+            coroutine_drop: None,
+            coroutine_layout: None,
+        }
+    }
+}
+
 /// The lowered representation of a single function.
 #[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct Body<'tcx> {
@@ -365,7 +384,7 @@ impl<'tcx> Body<'tcx> {
         arg_count: usize,
         var_debug_info: Vec<VarDebugInfo<'tcx>>,
         span: Span,
-        coroutine_kind: Option<CoroutineKind>,
+        coroutine: Option<Box<CoroutineInfo<'tcx>>>,
         tainted_by_errors: Option<ErrorGuaranteed>,
     ) -> Self {
         // We need `arg_count` locals, and one for the return place.
@@ -382,14 +401,7 @@ impl<'tcx> Body<'tcx> {
             source,
             basic_blocks: BasicBlocks::new(basic_blocks),
             source_scopes,
-            coroutine: coroutine_kind.map(|coroutine_kind| {
-                Box::new(CoroutineInfo {
-                    yield_ty: None,
-                    coroutine_drop: None,
-                    coroutine_layout: None,
-                    coroutine_kind,
-                })
-            }),
+            coroutine,
             local_decls,
             user_type_annotations,
             arg_count,
@@ -552,6 +564,11 @@ impl<'tcx> Body<'tcx> {
     }
 
     #[inline]
+    pub fn resume_ty(&self) -> Option<Ty<'tcx>> {
+        self.coroutine.as_ref().and_then(|coroutine| coroutine.resume_ty)
+    }
+
+    #[inline]
     pub fn coroutine_layout(&self) -> Option<&CoroutineLayout<'tcx>> {
         self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_layout.as_ref())
     }
@@ -720,7 +737,7 @@ pub struct SourceInfo {
     pub span: Span,
 
     /// The source scope, keeping track of which bindings can be
-    /// seen by debuginfo, active lint levels, `unsafe {...}`, etc.
+    /// seen by debuginfo, active lint levels, etc.
     pub scope: SourceScope,
 }
 
@@ -942,7 +959,7 @@ pub struct LocalDecl<'tcx> {
 
 /// Extra information about a some locals that's used for diagnostics and for
 /// classifying variables into local variables, statics, etc, which is needed e.g.
-/// for unsafety checking.
+/// for borrow checking.
 ///
 /// Not used for non-StaticRef temporaries, the return place, or anonymous
 /// function parameters.
@@ -1299,6 +1316,7 @@ impl<'tcx> BasicBlockData<'tcx> {
     }
 
     /// Does the block have no statements and an unreachable terminator?
+    #[inline]
     pub fn is_empty_unreachable(&self) -> bool {
         self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
     }
@@ -1568,6 +1586,7 @@ impl Location {
     ///
     /// Note that if this location represents a terminator, then the
     /// resulting location would be out of bounds and invalid.
+    #[inline]
     pub fn successor_within_block(&self) -> Location {
         Location { block: self.block, statement_index: self.statement_index + 1 }
     }
@@ -1604,6 +1623,7 @@ impl Location {
         false
     }
 
+    #[inline]
     pub fn dominates(&self, other: Location, dominators: &Dominators<BasicBlock>) -> bool {
         if self.block == other.block {
             self.statement_index <= other.statement_index
@@ -1623,6 +1643,7 @@ pub enum DefLocation {
 }
 
 impl DefLocation {
+    #[inline]
     pub fn dominates(self, location: Location, dominators: &Dominators<BasicBlock>) -> bool {
         match self {
             DefLocation::Argument => true,
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 0b487eae36d..a1e5d73a0fd 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -5,7 +5,6 @@ use std::io::{self, Write as _};
 use std::path::{Path, PathBuf};
 
 use super::graphviz::write_mir_fn_graphviz;
-use super::spanview::write_mir_fn_spanview;
 use rustc_ast::InlineAsmTemplatePiece;
 use rustc_middle::mir::interpret::{
     alloc_range, read_target_uint, AllocBytes, AllocId, Allocation, GlobalAlloc, Pointer,
@@ -141,16 +140,6 @@ fn dump_matched_mir_node<'tcx, F>(
             write_mir_fn_graphviz(tcx, body, false, &mut file)?;
         };
     }
-
-    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);
-            let mut file = create_dump_file_with_basename(tcx, &file_basename, "html")?;
-            if body.source.def_id().is_local() {
-                write_mir_fn_spanview(tcx, body, spanview, &file_basename, &mut file)?;
-            }
-        };
-    }
 }
 
 /// Returns the file basename portion (without extension) of a filename path
diff --git a/compiler/rustc_middle/src/mir/spanview.rs b/compiler/rustc_middle/src/mir/spanview.rs
deleted file mode 100644
index cb9fc0d37f2..00000000000
--- a/compiler/rustc_middle/src/mir/spanview.rs
+++ /dev/null
@@ -1,642 +0,0 @@
-use rustc_middle::hir;
-use rustc_middle::mir::*;
-use rustc_session::config::MirSpanview;
-use rustc_span::{BytePos, Pos};
-
-use std::cmp;
-use std::io::{self, Write};
-
-pub const TOOLTIP_INDENT: &str = "    ";
-
-const CARET: char = '\u{2038}'; // Unicode `CARET`
-const ANNOTATION_LEFT_BRACKET: char = '\u{298a}'; // Unicode `Z NOTATION RIGHT BINDING BRACKET`
-const ANNOTATION_RIGHT_BRACKET: char = '\u{2989}'; // Unicode `Z NOTATION LEFT BINDING BRACKET`
-const NEW_LINE_SPAN: &str = "</span>\n<span class=\"line\">";
-const HEADER: &str = r#"<!DOCTYPE html>
-<html lang="en">
-<head>
-<meta charset="utf-8">"#;
-const START_BODY: &str = r#"</head>
-<body>"#;
-const FOOTER: &str = r#"</body>
-</html>"#;
-
-const STYLE_SECTION: &str = r#"<style>
-    .line {
-        counter-increment: line;
-    }
-    .line:before {
-        content: counter(line) ": ";
-        font-family: Menlo, Monaco, monospace;
-        font-style: italic;
-        width: 3.8em;
-        display: inline-block;
-        text-align: right;
-        filter: opacity(50%);
-        -webkit-user-select: none;
-    }
-    .code {
-        color: #dddddd;
-        background-color: #222222;
-        font-family: Menlo, Monaco, monospace;
-        line-height: 1.4em;
-        border-bottom: 2px solid #222222;
-        white-space: pre;
-        display: inline-block;
-    }
-    .odd {
-        background-color: #55bbff;
-        color: #223311;
-    }
-    .even {
-        background-color: #ee7756;
-        color: #551133;
-    }
-    .code {
-        --index: calc(var(--layer) - 1);
-        padding-top: calc(var(--index) * 0.15em);
-        filter:
-            hue-rotate(calc(var(--index) * 25deg))
-            saturate(calc(100% - (var(--index) * 2%)))
-            brightness(calc(100% - (var(--index) * 1.5%)));
-    }
-    .annotation {
-        color: #4444ff;
-        font-family: monospace;
-        font-style: italic;
-        display: none;
-        -webkit-user-select: none;
-    }
-    body:active .annotation {
-        /* requires holding mouse down anywhere on the page */
-        display: inline-block;
-    }
-    span:hover .annotation {
-        /* requires hover over a span ONLY on its first line */
-        display: inline-block;
-    }
-</style>"#;
-
-/// Metadata to highlight the span of a MIR BasicBlock, Statement, or Terminator.
-#[derive(Clone, Debug)]
-pub struct SpanViewable {
-    pub bb: BasicBlock,
-    pub span: Span,
-    pub id: String,
-    pub tooltip: String,
-}
-
-/// Write a spanview HTML+CSS file to analyze MIR element spans.
-pub fn write_mir_fn_spanview<'tcx, W>(
-    tcx: TyCtxt<'tcx>,
-    body: &Body<'tcx>,
-    spanview: MirSpanview,
-    title: &str,
-    w: &mut W,
-) -> io::Result<()>
-where
-    W: Write,
-{
-    let def_id = body.source.def_id();
-    let hir_body = hir_body(tcx, def_id);
-    if hir_body.is_none() {
-        return Ok(());
-    }
-    let body_span = hir_body.unwrap().value.span;
-    let mut span_viewables = Vec::new();
-    for (bb, data) in body.basic_blocks.iter_enumerated() {
-        match spanview {
-            MirSpanview::Statement => {
-                for (i, statement) in data.statements.iter().enumerate() {
-                    if let Some(span_viewable) =
-                        statement_span_viewable(tcx, body_span, bb, i, statement)
-                    {
-                        span_viewables.push(span_viewable);
-                    }
-                }
-                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
-                    span_viewables.push(span_viewable);
-                }
-            }
-            MirSpanview::Terminator => {
-                if let Some(span_viewable) = terminator_span_viewable(tcx, body_span, bb, data) {
-                    span_viewables.push(span_viewable);
-                }
-            }
-            MirSpanview::Block => {
-                if let Some(span_viewable) = block_span_viewable(tcx, body_span, bb, data) {
-                    span_viewables.push(span_viewable);
-                }
-            }
-        }
-    }
-    write_document(tcx, fn_span(tcx, def_id), span_viewables, title, w)?;
-    Ok(())
-}
-
-/// Generate a spanview HTML+CSS document for the given local function `def_id`, and a pre-generated
-/// list `SpanViewable`s.
-pub fn write_document<'tcx, W>(
-    tcx: TyCtxt<'tcx>,
-    spanview_span: Span,
-    mut span_viewables: Vec<SpanViewable>,
-    title: &str,
-    w: &mut W,
-) -> io::Result<()>
-where
-    W: Write,
-{
-    let mut from_pos = spanview_span.lo();
-    let end_pos = spanview_span.hi();
-    let source_map = tcx.sess.source_map();
-    let start = source_map.lookup_char_pos(from_pos);
-    let indent_to_initial_start_col = " ".repeat(start.col.to_usize());
-    debug!(
-        "spanview_span={:?}; source is:\n{}{}",
-        spanview_span,
-        indent_to_initial_start_col,
-        source_map.span_to_snippet(spanview_span).expect("function should have printable source")
-    );
-    writeln!(w, "{HEADER}")?;
-    writeln!(w, "<title>{title}</title>")?;
-    writeln!(w, "{STYLE_SECTION}")?;
-    writeln!(w, "{START_BODY}")?;
-    write!(
-        w,
-        r#"<div class="code" style="counter-reset: line {}"><span class="line">{}"#,
-        start.line - 1,
-        indent_to_initial_start_col,
-    )?;
-    span_viewables.sort_unstable_by(|a, b| {
-        let a = a.span;
-        let b = b.span;
-        if a.lo() == b.lo() {
-            // Sort hi() in reverse order so shorter spans are attempted after longer spans.
-            // This should give shorter spans a higher "layer", so they are not covered by
-            // the longer spans.
-            b.hi().partial_cmp(&a.hi())
-        } else {
-            a.lo().partial_cmp(&b.lo())
-        }
-        .unwrap()
-    });
-    let mut ordered_viewables = &span_viewables[..];
-    const LOWEST_VIEWABLE_LAYER: usize = 1;
-    let mut alt = false;
-    while ordered_viewables.len() > 0 {
-        debug!(
-            "calling write_next_viewable with from_pos={}, end_pos={}, and viewables len={}",
-            from_pos.to_usize(),
-            end_pos.to_usize(),
-            ordered_viewables.len()
-        );
-        let curr_id = &ordered_viewables[0].id;
-        let (next_from_pos, next_ordered_viewables) = write_next_viewable_with_overlaps(
-            tcx,
-            from_pos,
-            end_pos,
-            ordered_viewables,
-            alt,
-            LOWEST_VIEWABLE_LAYER,
-            w,
-        )?;
-        debug!(
-            "DONE calling write_next_viewable, with new from_pos={}, \
-             and remaining viewables len={}",
-            next_from_pos.to_usize(),
-            next_ordered_viewables.len()
-        );
-        assert!(
-            from_pos != next_from_pos || ordered_viewables.len() != next_ordered_viewables.len(),
-            "write_next_viewable_with_overlaps() must make a state change"
-        );
-        from_pos = next_from_pos;
-        if next_ordered_viewables.len() != ordered_viewables.len() {
-            ordered_viewables = next_ordered_viewables;
-            if let Some(next_ordered_viewable) = ordered_viewables.first() {
-                if &next_ordered_viewable.id != curr_id {
-                    alt = !alt;
-                }
-            }
-        }
-    }
-    if from_pos < end_pos {
-        write_coverage_gap(tcx, from_pos, end_pos, w)?;
-    }
-    writeln!(w, r#"</span></div>"#)?;
-    writeln!(w, "{FOOTER}")?;
-    Ok(())
-}
-
-/// Format a string showing the start line and column, and end line and column within a file.
-pub fn source_range_no_file(tcx: TyCtxt<'_>, span: Span) -> String {
-    let source_map = tcx.sess.source_map();
-    let start = source_map.lookup_char_pos(span.lo());
-    let end = source_map.lookup_char_pos(span.hi());
-    format!("{}:{}-{}:{}", start.line, start.col.to_usize() + 1, end.line, end.col.to_usize() + 1)
-}
-
-fn statement_span_viewable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body_span: Span,
-    bb: BasicBlock,
-    i: usize,
-    statement: &Statement<'tcx>,
-) -> Option<SpanViewable> {
-    let span = statement.source_info.span;
-    if !body_span.contains(span) {
-        return None;
-    }
-    let id = format!("{}[{}]", bb.index(), i);
-    let tooltip = tooltip(tcx, &id, span, vec![statement.clone()], &None);
-    Some(SpanViewable { bb, span, id, tooltip })
-}
-
-fn terminator_span_viewable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body_span: Span,
-    bb: BasicBlock,
-    data: &BasicBlockData<'tcx>,
-) -> Option<SpanViewable> {
-    let term = data.terminator();
-    let span = term.source_info.span;
-    if !body_span.contains(span) {
-        return None;
-    }
-    let id = format!("{}:{}", bb.index(), term.kind.name());
-    let tooltip = tooltip(tcx, &id, span, vec![], &data.terminator);
-    Some(SpanViewable { bb, span, id, tooltip })
-}
-
-fn block_span_viewable<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    body_span: Span,
-    bb: BasicBlock,
-    data: &BasicBlockData<'tcx>,
-) -> Option<SpanViewable> {
-    let span = compute_block_span(data, body_span);
-    if !body_span.contains(span) {
-        return None;
-    }
-    let id = format!("{}", bb.index());
-    let tooltip = tooltip(tcx, &id, span, data.statements.clone(), &data.terminator);
-    Some(SpanViewable { bb, span, id, tooltip })
-}
-
-fn compute_block_span(data: &BasicBlockData<'_>, body_span: Span) -> Span {
-    let mut span = data.terminator().source_info.span;
-    for statement_span in data.statements.iter().map(|statement| statement.source_info.span) {
-        // Only combine Spans from the root context, and within the function's body_span.
-        if statement_span.ctxt().is_root() && body_span.contains(statement_span) {
-            span = span.to(statement_span);
-        }
-    }
-    span
-}
-
-/// Recursively process each ordered span. Spans that overlap will have progressively varying
-/// styles, such as increased padding for each overlap. Non-overlapping adjacent spans will
-/// have alternating style choices, to help distinguish between them if, visually adjacent.
-/// The `layer` is incremented for each overlap, and the `alt` bool alternates between true
-/// and false, for each adjacent non-overlapping span. Source code between the spans (code
-/// that is not in any coverage region) has neutral styling.
-fn write_next_viewable_with_overlaps<'tcx, 'b, W>(
-    tcx: TyCtxt<'tcx>,
-    mut from_pos: BytePos,
-    mut to_pos: BytePos,
-    ordered_viewables: &'b [SpanViewable],
-    alt: bool,
-    layer: usize,
-    w: &mut W,
-) -> io::Result<(BytePos, &'b [SpanViewable])>
-where
-    W: Write,
-{
-    let debug_indent = "  ".repeat(layer);
-    let (viewable, mut remaining_viewables) =
-        ordered_viewables.split_first().expect("ordered_viewables should have some");
-
-    if from_pos < viewable.span.lo() {
-        debug!(
-            "{}advance from_pos to next SpanViewable (from from_pos={} to viewable.span.lo()={} \
-             of {:?}), with to_pos={}",
-            debug_indent,
-            from_pos.to_usize(),
-            viewable.span.lo().to_usize(),
-            viewable.span,
-            to_pos.to_usize()
-        );
-        let hi = cmp::min(viewable.span.lo(), to_pos);
-        write_coverage_gap(tcx, from_pos, hi, w)?;
-        from_pos = hi;
-        if from_pos < viewable.span.lo() {
-            debug!(
-                "{}EARLY RETURN: stopped before getting to next SpanViewable, at {}",
-                debug_indent,
-                from_pos.to_usize()
-            );
-            return Ok((from_pos, ordered_viewables));
-        }
-    }
-
-    if from_pos < viewable.span.hi() {
-        // Set to_pos to the end of this `viewable` to ensure the recursive calls stop writing
-        // with room to print the tail.
-        to_pos = cmp::min(viewable.span.hi(), to_pos);
-        debug!(
-            "{}update to_pos (if not closer) to viewable.span.hi()={}; to_pos is now {}",
-            debug_indent,
-            viewable.span.hi().to_usize(),
-            to_pos.to_usize()
-        );
-    }
-
-    let mut subalt = false;
-    while remaining_viewables.len() > 0 && remaining_viewables[0].span.overlaps(viewable.span) {
-        let overlapping_viewable = &remaining_viewables[0];
-        debug!("{}overlapping_viewable.span={:?}", debug_indent, overlapping_viewable.span);
-
-        let span =
-            trim_span(viewable.span, from_pos, cmp::min(overlapping_viewable.span.lo(), to_pos));
-        let mut some_html_snippet = if from_pos <= viewable.span.hi() || viewable.span.is_empty() {
-            // `viewable` is not yet fully rendered, so start writing the span, up to either the
-            // `to_pos` or the next `overlapping_viewable`, whichever comes first.
-            debug!(
-                "{}make html_snippet (may not write it if early exit) for partial span {:?} \
-                 of viewable.span {:?}",
-                debug_indent, span, viewable.span
-            );
-            from_pos = span.hi();
-            make_html_snippet(tcx, span, Some(viewable))
-        } else {
-            None
-        };
-
-        // Defer writing the HTML snippet (until after early return checks) ONLY for empty spans.
-        // An empty Span with Some(html_snippet) is probably a tail marker. If there is an early
-        // exit, there should be another opportunity to write the tail marker.
-        if !span.is_empty() {
-            if let Some(ref html_snippet) = some_html_snippet {
-                debug!(
-                    "{}write html_snippet for that partial span of viewable.span {:?}",
-                    debug_indent, viewable.span
-                );
-                write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
-            }
-            some_html_snippet = None;
-        }
-
-        if from_pos < overlapping_viewable.span.lo() {
-            debug!(
-                "{}EARLY RETURN: from_pos={} has not yet reached the \
-                 overlapping_viewable.span {:?}",
-                debug_indent,
-                from_pos.to_usize(),
-                overlapping_viewable.span
-            );
-            // must have reached `to_pos` before reaching the start of the
-            // `overlapping_viewable.span`
-            return Ok((from_pos, ordered_viewables));
-        }
-
-        if from_pos == to_pos
-            && !(from_pos == overlapping_viewable.span.lo() && overlapping_viewable.span.is_empty())
-        {
-            debug!(
-                "{}EARLY RETURN: from_pos=to_pos={} and overlapping_viewable.span {:?} is not \
-                 empty, or not from_pos",
-                debug_indent,
-                to_pos.to_usize(),
-                overlapping_viewable.span
-            );
-            // `to_pos` must have occurred before the overlapping viewable. Return
-            // `ordered_viewables` so we can continue rendering the `viewable`, from after the
-            // `to_pos`.
-            return Ok((from_pos, ordered_viewables));
-        }
-
-        if let Some(ref html_snippet) = some_html_snippet {
-            debug!(
-                "{}write html_snippet for that partial span of viewable.span {:?}",
-                debug_indent, viewable.span
-            );
-            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
-        }
-
-        debug!(
-            "{}recursively calling write_next_viewable with from_pos={}, to_pos={}, \
-             and viewables len={}",
-            debug_indent,
-            from_pos.to_usize(),
-            to_pos.to_usize(),
-            remaining_viewables.len()
-        );
-        // Write the overlaps (and the overlaps' overlaps, if any) up to `to_pos`.
-        let curr_id = &remaining_viewables[0].id;
-        let (next_from_pos, next_remaining_viewables) = write_next_viewable_with_overlaps(
-            tcx,
-            from_pos,
-            to_pos,
-            remaining_viewables,
-            subalt,
-            layer + 1,
-            w,
-        )?;
-        debug!(
-            "{}DONE recursively calling write_next_viewable, with new from_pos={}, and remaining \
-             viewables len={}",
-            debug_indent,
-            next_from_pos.to_usize(),
-            next_remaining_viewables.len()
-        );
-        assert!(
-            from_pos != next_from_pos
-                || remaining_viewables.len() != next_remaining_viewables.len(),
-            "write_next_viewable_with_overlaps() must make a state change"
-        );
-        from_pos = next_from_pos;
-        if next_remaining_viewables.len() != remaining_viewables.len() {
-            remaining_viewables = next_remaining_viewables;
-            if let Some(next_ordered_viewable) = remaining_viewables.first() {
-                if &next_ordered_viewable.id != curr_id {
-                    subalt = !subalt;
-                }
-            }
-        }
-    }
-    if from_pos <= viewable.span.hi() {
-        let span = trim_span(viewable.span, from_pos, to_pos);
-        debug!(
-            "{}After overlaps, writing (end span?) {:?} of viewable.span {:?}",
-            debug_indent, span, viewable.span
-        );
-        if let Some(ref html_snippet) = make_html_snippet(tcx, span, Some(viewable)) {
-            from_pos = span.hi();
-            write_span(html_snippet, &viewable.tooltip, alt, layer, w)?;
-        }
-    }
-    debug!("{}RETURN: No more overlap", debug_indent);
-    Ok((
-        from_pos,
-        if from_pos < viewable.span.hi() { ordered_viewables } else { remaining_viewables },
-    ))
-}
-
-#[inline(always)]
-fn write_coverage_gap<W>(tcx: TyCtxt<'_>, lo: BytePos, hi: BytePos, w: &mut W) -> io::Result<()>
-where
-    W: Write,
-{
-    let span = Span::with_root_ctxt(lo, hi);
-    if let Some(ref html_snippet) = make_html_snippet(tcx, span, None) {
-        write_span(html_snippet, "", false, 0, w)
-    } else {
-        Ok(())
-    }
-}
-
-fn write_span<W>(
-    html_snippet: &str,
-    tooltip: &str,
-    alt: bool,
-    layer: usize,
-    w: &mut W,
-) -> io::Result<()>
-where
-    W: Write,
-{
-    let maybe_alt_class = if layer > 0 { if alt { " odd" } else { " even" } } else { "" };
-    let maybe_title_attr = if !tooltip.is_empty() {
-        format!(" title=\"{}\"", escape_attr(tooltip))
-    } else {
-        "".to_owned()
-    };
-    if layer == 1 {
-        write!(w, "<span>")?;
-    }
-    for (i, line) in html_snippet.lines().enumerate() {
-        if i > 0 {
-            write!(w, "{NEW_LINE_SPAN}")?;
-        }
-        write!(
-            w,
-            r#"<span class="code{maybe_alt_class}" style="--layer: {layer}"{maybe_title_attr}>{line}</span>"#
-        )?;
-    }
-    // Check for and translate trailing newlines, because `str::lines()` ignores them
-    if html_snippet.ends_with('\n') {
-        write!(w, "{NEW_LINE_SPAN}")?;
-    }
-    if layer == 1 {
-        write!(w, "</span>")?;
-    }
-    Ok(())
-}
-
-fn make_html_snippet(
-    tcx: TyCtxt<'_>,
-    span: Span,
-    some_viewable: Option<&SpanViewable>,
-) -> Option<String> {
-    let source_map = tcx.sess.source_map();
-    let snippet = source_map
-        .span_to_snippet(span)
-        .unwrap_or_else(|err| bug!("span_to_snippet error for span {:?}: {:?}", span, err));
-    let html_snippet = if let Some(viewable) = some_viewable {
-        let is_head = span.lo() == viewable.span.lo();
-        let is_tail = span.hi() == viewable.span.hi();
-        let mut labeled_snippet = if is_head {
-            format!(r#"<span class="annotation">{}{}</span>"#, viewable.id, ANNOTATION_LEFT_BRACKET)
-        } else {
-            "".to_owned()
-        };
-        if span.is_empty() {
-            if is_head && is_tail {
-                labeled_snippet.push(CARET);
-            }
-        } else {
-            labeled_snippet.push_str(&escape_html(&snippet));
-        };
-        if is_tail {
-            labeled_snippet.push_str(&format!(
-                r#"<span class="annotation">{}{}</span>"#,
-                ANNOTATION_RIGHT_BRACKET, viewable.id
-            ));
-        }
-        labeled_snippet
-    } else {
-        escape_html(&snippet)
-    };
-    if html_snippet.is_empty() { None } else { Some(html_snippet) }
-}
-
-fn tooltip<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    spanview_id: &str,
-    span: Span,
-    statements: Vec<Statement<'tcx>>,
-    terminator: &Option<Terminator<'tcx>>,
-) -> String {
-    let source_map = tcx.sess.source_map();
-    let mut text = Vec::new();
-    text.push(format!("{}: {}:", spanview_id, &source_map.span_to_embeddable_string(span)));
-    for statement in statements {
-        let source_range = source_range_no_file(tcx, statement.source_info.span);
-        text.push(format!(
-            "\n{}{}: {}: {:?}",
-            TOOLTIP_INDENT,
-            source_range,
-            statement.kind.name(),
-            statement
-        ));
-    }
-    if let Some(term) = terminator {
-        let source_range = source_range_no_file(tcx, term.source_info.span);
-        text.push(format!(
-            "\n{}{}: {}: {:?}",
-            TOOLTIP_INDENT,
-            source_range,
-            term.kind.name(),
-            term.kind
-        ));
-    }
-    text.join("")
-}
-
-fn trim_span(span: Span, from_pos: BytePos, to_pos: BytePos) -> Span {
-    trim_span_hi(trim_span_lo(span, from_pos), to_pos)
-}
-
-fn trim_span_lo(span: Span, from_pos: BytePos) -> Span {
-    if from_pos <= span.lo() { span } else { span.with_lo(cmp::min(span.hi(), from_pos)) }
-}
-
-fn trim_span_hi(span: Span, to_pos: BytePos) -> Span {
-    if to_pos >= span.hi() { span } else { span.with_hi(cmp::max(span.lo(), to_pos)) }
-}
-
-fn fn_span(tcx: TyCtxt<'_>, def_id: DefId) -> Span {
-    let fn_decl_span = tcx.def_span(def_id);
-    if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) {
-        if fn_decl_span.eq_ctxt(body_span) { fn_decl_span.to(body_span) } else { body_span }
-    } else {
-        fn_decl_span
-    }
-}
-
-fn hir_body(tcx: TyCtxt<'_>, def_id: DefId) -> Option<&rustc_hir::Body<'_>> {
-    let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
-    hir::map::associated_body(hir_node).map(|(_, fn_body_id)| tcx.hir().body(fn_body_id))
-}
-
-fn escape_html(s: &str) -> String {
-    s.replace('&', "&amp;").replace('<', "&lt;").replace('>', "&gt;")
-}
-
-fn escape_attr(s: &str) -> String {
-    s.replace('&', "&amp;")
-        .replace('\"', "&quot;")
-        .replace('\'', "&#39;")
-        .replace('<', "&lt;")
-        .replace('>', "&gt;")
-}
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index 7be6deb6141..385237b357b 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -26,6 +26,7 @@ impl SwitchTargets {
     }
 
     /// Inverse of `SwitchTargets::static_if`.
+    #[inline]
     pub fn as_static_if(&self) -> Option<(u128, BasicBlock, BasicBlock)> {
         if let &[value] = &self.values[..]
             && let &[then, else_] = &self.targets[..]
@@ -37,6 +38,7 @@ impl SwitchTargets {
     }
 
     /// Returns the fallback target that is jumped to when none of the values match the operand.
+    #[inline]
     pub fn otherwise(&self) -> BasicBlock {
         *self.targets.last().unwrap()
     }
@@ -47,15 +49,18 @@ impl SwitchTargets {
     /// including the `otherwise` fallback target.
     ///
     /// Note that this may yield 0 elements. Only the `otherwise` branch is mandatory.
+    #[inline]
     pub fn iter(&self) -> SwitchTargetsIter<'_> {
         SwitchTargetsIter { inner: iter::zip(&self.values, &self.targets) }
     }
 
     /// Returns a slice with all possible jump targets (including the fallback target).
+    #[inline]
     pub fn all_targets(&self) -> &[BasicBlock] {
         &self.targets
     }
 
+    #[inline]
     pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
         &mut self.targets
     }
@@ -63,6 +68,7 @@ impl SwitchTargets {
     /// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
     /// specific value. This cannot fail, as it'll return the `otherwise`
     /// branch if there's not a specific match for the value.
+    #[inline]
     pub fn target_for_value(&self, value: u128) -> BasicBlock {
         self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
     }
@@ -75,10 +81,12 @@ pub struct SwitchTargetsIter<'a> {
 impl<'a> Iterator for SwitchTargetsIter<'a> {
     type Item = (u128, BasicBlock);
 
+    #[inline]
     fn next(&mut self) -> Option<Self::Item> {
         self.inner.next().map(|(val, bb)| (*val, *bb))
     }
 
+    #[inline]
     fn size_hint(&self) -> (usize, Option<usize>) {
         self.inner.size_hint()
     }
@@ -330,28 +338,34 @@ pub type SuccessorsMut<'a> =
     iter::Chain<std::option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
 
 impl<'tcx> Terminator<'tcx> {
+    #[inline]
     pub fn successors(&self) -> Successors<'_> {
         self.kind.successors()
     }
 
+    #[inline]
     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
         self.kind.successors_mut()
     }
 
+    #[inline]
     pub fn unwind(&self) -> Option<&UnwindAction> {
         self.kind.unwind()
     }
 
+    #[inline]
     pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
         self.kind.unwind_mut()
     }
 }
 
 impl<'tcx> TerminatorKind<'tcx> {
+    #[inline]
     pub fn if_(cond: Operand<'tcx>, t: BasicBlock, f: BasicBlock) -> TerminatorKind<'tcx> {
         TerminatorKind::SwitchInt { discr: cond, targets: SwitchTargets::static_if(0, f, t) }
     }
 
+    #[inline]
     pub fn successors(&self) -> Successors<'_> {
         use self::TerminatorKind::*;
         match *self {
@@ -392,6 +406,7 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 
+    #[inline]
     pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
         use self::TerminatorKind::*;
         match *self {
@@ -430,6 +445,7 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 
+    #[inline]
     pub fn unwind(&self) -> Option<&UnwindAction> {
         match *self {
             TerminatorKind::Goto { .. }
@@ -449,6 +465,7 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 
+    #[inline]
     pub fn unwind_mut(&mut self) -> Option<&mut UnwindAction> {
         match *self {
             TerminatorKind::Goto { .. }
@@ -468,6 +485,7 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 
+    #[inline]
     pub fn as_switch(&self) -> Option<(&Operand<'tcx>, &SwitchTargets)> {
         match self {
             TerminatorKind::SwitchInt { discr, targets } => Some((discr, targets)),
@@ -475,6 +493,7 @@ impl<'tcx> TerminatorKind<'tcx> {
         }
     }
 
+    #[inline]
     pub fn as_goto(&self) -> Option<BasicBlock> {
         match self {
             TerminatorKind::Goto { target } => Some(*target),
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 132ecf91af1..2ccf5a9f6f7 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -996,6 +996,12 @@ macro_rules! super_body {
                     TyContext::YieldTy(SourceInfo::outermost(span))
                 );
             }
+            if let Some(resume_ty) = $(& $mutability)? gen.resume_ty {
+                $self.visit_ty(
+                    resume_ty,
+                    TyContext::ResumeTy(SourceInfo::outermost(span))
+                );
+            }
         }
 
         for (bb, data) in basic_blocks_iter!($body, $($mutability, $invalidate)?) {
@@ -1244,6 +1250,8 @@ pub enum TyContext {
 
     YieldTy(SourceInfo),
 
+    ResumeTy(SourceInfo),
+
     /// A type found at some location.
     Location(Location),
 }
diff --git a/compiler/rustc_middle/src/query/keys.rs b/compiler/rustc_middle/src/query/keys.rs
index 11376345052..945f17d5df2 100644
--- a/compiler/rustc_middle/src/query/keys.rs
+++ b/compiler/rustc_middle/src/query/keys.rs
@@ -40,7 +40,7 @@ pub trait Key: Sized {
         None
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
+    fn ty_def_id(&self) -> Option<DefId> {
         None
     }
 }
@@ -406,9 +406,10 @@ impl<'tcx> Key for Ty<'tcx> {
         DUMMY_SP
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
-        match self.kind() {
+    fn ty_def_id(&self) -> Option<DefId> {
+        match *self.kind() {
             ty::Adt(adt, _) => Some(adt.did()),
+            ty::Coroutine(def_id, ..) => Some(def_id),
             _ => None,
         }
     }
@@ -452,6 +453,10 @@ impl<'tcx, T: Key> Key for ty::ParamEnvAnd<'tcx, T> {
     fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
         self.value.default_span(tcx)
     }
+
+    fn ty_def_id(&self) -> Option<DefId> {
+        self.value.ty_def_id()
+    }
 }
 
 impl Key for Symbol {
@@ -550,7 +555,7 @@ impl<'tcx> Key for (ValidityRequirement, ty::ParamEnvAnd<'tcx, Ty<'tcx>>) {
         DUMMY_SP
     }
 
-    fn ty_adt_id(&self) -> Option<DefId> {
+    fn ty_def_id(&self) -> Option<DefId> {
         match self.1.value.kind() {
             ty::Adt(adt, _) => Some(adt.did()),
             _ => None,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 3a54f5f6b3d..0e3b9984423 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -57,11 +57,11 @@ use rustc_ast as ast;
 use rustc_ast::expand::{allocator::AllocatorKind, StrippedCfgItem};
 use rustc_attr as attr;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
-use rustc_data_structures::unord::UnordSet;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, DocLinkResMap};
@@ -264,7 +264,7 @@ rustc_queries! {
     }
 
     query collect_return_position_impl_trait_in_trait_tys(key: DefId)
-        -> Result<&'tcx FxHashMap<DefId, ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed>
+        -> Result<&'tcx DefIdMap<ty::EarlyBinder<Ty<'tcx>>>, ErrorGuaranteed>
     {
         desc { "comparing an impl and trait method signature, inferring any hidden `impl Trait` types in the process" }
         cache_on_disk_if { key.is_local() }
@@ -611,7 +611,7 @@ rustc_queries! {
         desc { "erasing regions from `{}`", ty }
     }
 
-    query wasm_import_module_map(_: CrateNum) -> &'tcx FxHashMap<DefId, String> {
+    query wasm_import_module_map(_: CrateNum) -> &'tcx DefIdMap<String> {
         arena_cache
         desc { "getting wasm import module map" }
     }
@@ -869,15 +869,14 @@ rustc_queries! {
         desc { |tcx| "collecting all inherent impls for `{:?}`", key }
     }
 
-    /// The result of unsafety-checking this `LocalDefId`.
-    query unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult {
+    /// The result of unsafety-checking this `LocalDefId` with the old checker.
+    query mir_unsafety_check_result(key: LocalDefId) -> &'tcx mir::UnsafetyCheckResult {
         desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { true }
     }
 
-    /// Unsafety-check this `LocalDefId` with THIR unsafeck. This should be
-    /// used with `-Zthir-unsafeck`.
-    query thir_check_unsafety(key: LocalDefId) {
+    /// Unsafety-check this `LocalDefId`.
+    query check_unsafety(key: LocalDefId) {
         desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { true }
     }
@@ -938,10 +937,6 @@ rustc_queries! {
         desc { |tcx| "checking naked functions in {}", describe_as_module(key, tcx) }
     }
 
-    query check_mod_item_types(key: LocalModDefId) -> () {
-        desc { |tcx| "checking item types in {}", describe_as_module(key, tcx) }
-    }
-
     query check_mod_privacy(key: LocalModDefId) -> () {
         desc { |tcx| "checking privacy in {}", describe_as_module(key.to_local_def_id(), tcx) }
     }
@@ -1392,6 +1387,8 @@ rustc_queries! {
     ) -> Result<ty::layout::TyAndLayout<'tcx>, &'tcx ty::layout::LayoutError<'tcx>> {
         depth_limit
         desc { "computing layout of `{}`", key.value }
+        // we emit our own error during query cycle handling
+        cycle_delay_bug
     }
 
     /// Compute a `FnAbi` suitable for indirect calls, i.e. to `fn` pointers.
@@ -1542,7 +1539,7 @@ rustc_queries! {
     /// added or removed in any upstream crate. Instead use the narrower
     /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even
     /// better, `Instance::upstream_monomorphization()`.
-    query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap<FxHashMap<GenericArgsRef<'tcx>, CrateNum>> {
+    query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap<UnordMap<GenericArgsRef<'tcx>, CrateNum>> {
         arena_cache
         desc { "collecting available upstream monomorphizations" }
     }
@@ -1555,7 +1552,7 @@ rustc_queries! {
     /// You likely want to call `Instance::upstream_monomorphization()`
     /// instead of invoking this query directly.
     query upstream_monomorphizations_for(def_id: DefId)
-        -> Option<&'tcx FxHashMap<GenericArgsRef<'tcx>, CrateNum>>
+        -> Option<&'tcx UnordMap<GenericArgsRef<'tcx>, CrateNum>>
     {
         desc { |tcx|
             "collecting available upstream monomorphizations for `{}`",
@@ -1667,7 +1664,7 @@ rustc_queries! {
         desc { "resolving lifetimes" }
     }
     query named_variable_map(_: hir::OwnerId) ->
-        Option<&'tcx FxHashMap<ItemLocalId, ResolvedArg>> {
+        Option<&'tcx FxIndexMap<ItemLocalId, ResolvedArg>> {
         desc { "looking up a named region" }
     }
     query is_late_bound_map(_: hir::OwnerId) -> Option<&'tcx FxIndexSet<ItemLocalId>> {
@@ -1683,7 +1680,7 @@ rustc_queries! {
         separate_provide_extern
     }
     query late_bound_vars_map(_: hir::OwnerId)
-        -> Option<&'tcx FxHashMap<ItemLocalId, Vec<ty::BoundVariableKind>>> {
+        -> Option<&'tcx FxIndexMap<ItemLocalId, Vec<ty::BoundVariableKind>>> {
         desc { "looking up late bound vars" }
     }
 
@@ -1739,7 +1736,7 @@ rustc_queries! {
         separate_provide_extern
         arena_cache
     }
-    query stability_implications(_: CrateNum) -> &'tcx FxHashMap<Symbol, Symbol> {
+    query stability_implications(_: CrateNum) -> &'tcx UnordMap<Symbol, Symbol> {
         arena_cache
         desc { "calculating the implications between `#[unstable]` features defined in a crate" }
         separate_provide_extern
@@ -1791,7 +1788,7 @@ rustc_queries! {
     }
     /// Collects the "trimmed", shortest accessible paths to all items for diagnostics.
     /// See the [provider docs](`rustc_middle::ty::print::trimmed_def_paths`) for more info.
-    query trimmed_def_paths(_: ()) -> &'tcx FxHashMap<DefId, Symbol> {
+    query trimmed_def_paths(_: ()) -> &'tcx DefIdMap<Symbol> {
         arena_cache
         desc { "calculating trimmed def paths" }
     }
@@ -2082,7 +2079,7 @@ rustc_queries! {
         desc { "computing autoderef types for `{}`", goal.value.value }
     }
 
-    query supported_target_features(_: CrateNum) -> &'tcx FxHashMap<String, Option<Symbol>> {
+    query supported_target_features(_: CrateNum) -> &'tcx UnordMap<String, Option<Symbol>> {
         arena_cache
         eval_always
         desc { "looking up supported target features" }
diff --git a/compiler/rustc_middle/src/query/on_disk_cache.rs b/compiler/rustc_middle/src/query/on_disk_cache.rs
index 0577d22d850..31db4ba62fb 100644
--- a/compiler/rustc_middle/src/query/on_disk_cache.rs
+++ b/compiler/rustc_middle/src/query/on_disk_cache.rs
@@ -2,7 +2,7 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, RwLock};
 use rustc_data_structures::unhash::UnhashMap;
-use rustc_data_structures::unord::UnordSet;
+use rustc_data_structures::unord::{UnordMap, UnordSet};
 use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LocalDefId, StableCrateId, LOCAL_CRATE};
 use rustc_hir::definitions::DefPathHash;
 use rustc_index::{Idx, IndexVec};
@@ -22,7 +22,8 @@ use rustc_span::hygiene::{
 };
 use rustc_span::source_map::SourceMap;
 use rustc_span::{
-    BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, StableSourceFileId,
+    BytePos, ExpnData, ExpnHash, Pos, RelativeBytePos, SourceFile, Span, SpanDecoder, SpanEncoder,
+    StableSourceFileId,
 };
 use rustc_span::{CachingSourceMapView, Symbol};
 use std::collections::hash_map::Entry;
@@ -582,10 +583,10 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Vec<u8> {
     }
 }
 
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for SyntaxContext {
-    fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self {
-        let syntax_contexts = decoder.syntax_contexts;
-        rustc_span::hygiene::decode_syntax_context(decoder, decoder.hygiene_context, |this, id| {
+impl<'a, 'tcx> SpanDecoder for CacheDecoder<'a, 'tcx> {
+    fn decode_syntax_context(&mut self) -> SyntaxContext {
+        let syntax_contexts = self.syntax_contexts;
+        rustc_span::hygiene::decode_syntax_context(self, self.hygiene_context, |this, id| {
             // This closure is invoked if we haven't already decoded the data for the `SyntaxContext` we are deserializing.
             // We look up the position of the associated `SyntaxData` and decode it.
             let pos = syntax_contexts.get(&id).unwrap();
@@ -595,11 +596,9 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for SyntaxContext {
             })
         })
     }
-}
 
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId {
-    fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self {
-        let hash = ExpnHash::decode(decoder);
+    fn decode_expn_id(&mut self) -> ExpnId {
+        let hash = ExpnHash::decode(self);
         if hash.is_root() {
             return ExpnId::root();
         }
@@ -608,23 +607,23 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId {
             return expn_id;
         }
 
-        let krate = decoder.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id());
+        let krate = self.tcx.stable_crate_id_to_crate_num(hash.stable_crate_id());
 
         let expn_id = if krate == LOCAL_CRATE {
             // We look up the position of the associated `ExpnData` and decode it.
-            let pos = decoder
+            let pos = self
                 .expn_data
                 .get(&hash)
-                .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, decoder.expn_data));
+                .unwrap_or_else(|| panic!("Bad hash {:?} (map {:?})", hash, self.expn_data));
 
-            let data: ExpnData = decoder
-                .with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA));
+            let data: ExpnData =
+                self.with_position(pos.to_usize(), |decoder| decode_tagged(decoder, TAG_EXPN_DATA));
             let expn_id = rustc_span::hygiene::register_local_expn_id(data, hash);
 
             #[cfg(debug_assertions)]
             {
                 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-                let local_hash = decoder.tcx.with_stable_hashing_context(|mut hcx| {
+                let local_hash = self.tcx.with_stable_hashing_context(|mut hcx| {
                     let mut hasher = StableHasher::new();
                     expn_id.expn_data().hash_stable(&mut hcx, &mut hasher);
                     hasher.finish()
@@ -634,9 +633,9 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId {
 
             expn_id
         } else {
-            let index_guess = decoder.foreign_expn_data[&hash];
-            decoder.tcx.cstore_untracked().expn_hash_to_expn_id(
-                decoder.tcx.sess,
+            let index_guess = self.foreign_expn_data[&hash];
+            self.tcx.cstore_untracked().expn_hash_to_expn_id(
+                self.tcx.sess,
                 krate,
                 index_guess,
                 hash,
@@ -646,21 +645,19 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for ExpnId {
         debug_assert_eq!(expn_id.krate, krate);
         expn_id
     }
-}
 
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Span {
-    fn decode(decoder: &mut CacheDecoder<'a, 'tcx>) -> Self {
-        let ctxt = SyntaxContext::decode(decoder);
-        let parent = Option::<LocalDefId>::decode(decoder);
-        let tag: u8 = Decodable::decode(decoder);
+    fn decode_span(&mut self) -> Span {
+        let ctxt = SyntaxContext::decode(self);
+        let parent = Option::<LocalDefId>::decode(self);
+        let tag: u8 = Decodable::decode(self);
 
         if tag == TAG_PARTIAL_SPAN {
             return Span::new(BytePos(0), BytePos(0), ctxt, parent);
         } else if tag == TAG_RELATIVE_SPAN {
-            let dlo = u32::decode(decoder);
-            let dto = u32::decode(decoder);
+            let dlo = u32::decode(self);
+            let dto = u32::decode(self);
 
-            let enclosing = decoder.tcx.source_span_untracked(parent.unwrap()).data_untracked();
+            let enclosing = self.tcx.source_span_untracked(parent.unwrap()).data_untracked();
             let span = Span::new(
                 enclosing.lo + BytePos::from_u32(dlo),
                 enclosing.lo + BytePos::from_u32(dto),
@@ -673,87 +670,81 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Span {
             debug_assert_eq!(tag, TAG_FULL_SPAN);
         }
 
-        let file_lo_index = SourceFileIndex::decode(decoder);
-        let line_lo = usize::decode(decoder);
-        let col_lo = RelativeBytePos::decode(decoder);
-        let len = BytePos::decode(decoder);
+        let file_lo_index = SourceFileIndex::decode(self);
+        let line_lo = usize::decode(self);
+        let col_lo = RelativeBytePos::decode(self);
+        let len = BytePos::decode(self);
 
-        let file_lo = decoder.file_index_to_file(file_lo_index);
+        let file_lo = self.file_index_to_file(file_lo_index);
         let lo = file_lo.lines()[line_lo - 1] + col_lo;
         let lo = file_lo.absolute_position(lo);
         let hi = lo + len;
 
         Span::new(lo, hi, ctxt, parent)
     }
-}
 
-// copy&paste impl from rustc_metadata
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for Symbol {
+    // copy&paste impl from rustc_metadata
     #[inline]
-    fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
-        let tag = d.read_u8();
+    fn decode_symbol(&mut self) -> Symbol {
+        let tag = self.read_u8();
 
         match tag {
             SYMBOL_STR => {
-                let s = d.read_str();
+                let s = self.read_str();
                 Symbol::intern(s)
             }
             SYMBOL_OFFSET => {
                 // read str offset
-                let pos = d.read_usize();
+                let pos = self.read_usize();
 
                 // move to str offset and read
-                d.opaque.with_position(pos, |d| {
+                self.opaque.with_position(pos, |d| {
                     let s = d.read_str();
                     Symbol::intern(s)
                 })
             }
             SYMBOL_PREINTERNED => {
-                let symbol_index = d.read_u32();
+                let symbol_index = self.read_u32();
                 Symbol::new_from_decoded(symbol_index)
             }
             _ => unreachable!(),
         }
     }
-}
 
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for CrateNum {
-    #[inline]
-    fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
-        let stable_id = StableCrateId::decode(d);
-        let cnum = d.tcx.stable_crate_id_to_crate_num(stable_id);
+    fn decode_crate_num(&mut self) -> CrateNum {
+        let stable_id = StableCrateId::decode(self);
+        let cnum = self.tcx.stable_crate_id_to_crate_num(stable_id);
         cnum
     }
-}
 
-// This impl makes sure that we get a runtime error when we try decode a
-// `DefIndex` that is not contained in a `DefId`. Such a case would be problematic
-// because we would not know how to transform the `DefIndex` to the current
-// context.
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefIndex {
-    fn decode(_d: &mut CacheDecoder<'a, 'tcx>) -> DefIndex {
+    // This impl makes sure that we get a runtime error when we try decode a
+    // `DefIndex` that is not contained in a `DefId`. Such a case would be problematic
+    // because we would not know how to transform the `DefIndex` to the current
+    // context.
+    fn decode_def_index(&mut self) -> DefIndex {
         panic!("trying to decode `DefIndex` outside the context of a `DefId`")
     }
-}
 
-// Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two
-// compilation sessions. We use the `DefPathHash`, which is stable across
-// sessions, to map the old `DefId` to the new one.
-impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for DefId {
-    #[inline]
-    fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
+    // Both the `CrateNum` and the `DefIndex` of a `DefId` can change in between two
+    // compilation sessions. We use the `DefPathHash`, which is stable across
+    // sessions, to map the old `DefId` to the new one.
+    fn decode_def_id(&mut self) -> DefId {
         // Load the `DefPathHash` which is was we encoded the `DefId` as.
-        let def_path_hash = DefPathHash::decode(d);
+        let def_path_hash = DefPathHash::decode(self);
 
         // Using the `DefPathHash`, we can lookup the new `DefId`.
         // Subtle: We only encode a `DefId` as part of a query result.
         // If we get to this point, then all of the query inputs were green,
         // which means that the definition with this hash is guaranteed to
         // still exist in the current compilation session.
-        d.tcx.def_path_hash_to_def_id(def_path_hash, &mut || {
+        self.tcx.def_path_hash_to_def_id(def_path_hash, &mut || {
             panic!("Failed to convert DefPathHash {def_path_hash:?}")
         })
     }
+
+    fn decode_attr_id(&mut self) -> rustc_span::AttrId {
+        panic!("cannot decode `AttrId` with `CacheDecoder`");
+    }
 }
 
 impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx UnordSet<LocalDefId> {
@@ -764,7 +755,7 @@ impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>> for &'tcx UnordSet<LocalDefId>
 }
 
 impl<'a, 'tcx> Decodable<CacheDecoder<'a, 'tcx>>
-    for &'tcx FxHashMap<DefId, ty::EarlyBinder<Ty<'tcx>>>
+    for &'tcx UnordMap<DefId, ty::EarlyBinder<Ty<'tcx>>>
 {
     #[inline]
     fn decode(d: &mut CacheDecoder<'a, 'tcx>) -> Self {
@@ -859,87 +850,93 @@ impl<'a, 'tcx> CacheEncoder<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for SyntaxContext {
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
-        rustc_span::hygiene::raw_encode_syntax_context(*self, s.hygiene_context, s);
+impl<'a, 'tcx> SpanEncoder for CacheEncoder<'a, 'tcx> {
+    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext) {
+        rustc_span::hygiene::raw_encode_syntax_context(syntax_context, self.hygiene_context, self);
     }
-}
 
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for ExpnId {
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
-        s.hygiene_context.schedule_expn_data_for_encoding(*self);
-        self.expn_hash().encode(s);
+    fn encode_expn_id(&mut self, expn_id: ExpnId) {
+        self.hygiene_context.schedule_expn_data_for_encoding(expn_id);
+        expn_id.expn_hash().encode(self);
     }
-}
 
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for Span {
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
-        let span_data = self.data_untracked();
-        span_data.ctxt.encode(s);
-        span_data.parent.encode(s);
+    fn encode_span(&mut self, span: Span) {
+        let span_data = span.data_untracked();
+        span_data.ctxt.encode(self);
+        span_data.parent.encode(self);
 
         if span_data.is_dummy() {
-            return TAG_PARTIAL_SPAN.encode(s);
+            return TAG_PARTIAL_SPAN.encode(self);
         }
 
         if let Some(parent) = span_data.parent {
-            let enclosing = s.tcx.source_span_untracked(parent).data_untracked();
+            let enclosing = self.tcx.source_span_untracked(parent).data_untracked();
             if enclosing.contains(span_data) {
-                TAG_RELATIVE_SPAN.encode(s);
-                (span_data.lo - enclosing.lo).to_u32().encode(s);
-                (span_data.hi - enclosing.lo).to_u32().encode(s);
+                TAG_RELATIVE_SPAN.encode(self);
+                (span_data.lo - enclosing.lo).to_u32().encode(self);
+                (span_data.hi - enclosing.lo).to_u32().encode(self);
                 return;
             }
         }
 
-        let pos = s.source_map.byte_pos_to_line_and_col(span_data.lo);
+        let pos = self.source_map.byte_pos_to_line_and_col(span_data.lo);
         let partial_span = match &pos {
             Some((file_lo, _, _)) => !file_lo.contains(span_data.hi),
             None => true,
         };
 
         if partial_span {
-            return TAG_PARTIAL_SPAN.encode(s);
+            return TAG_PARTIAL_SPAN.encode(self);
         }
 
         let (file_lo, line_lo, col_lo) = pos.unwrap();
 
         let len = span_data.hi - span_data.lo;
 
-        let source_file_index = s.source_file_index(file_lo);
+        let source_file_index = self.source_file_index(file_lo);
 
-        TAG_FULL_SPAN.encode(s);
-        source_file_index.encode(s);
-        line_lo.encode(s);
-        col_lo.encode(s);
-        len.encode(s);
+        TAG_FULL_SPAN.encode(self);
+        source_file_index.encode(self);
+        line_lo.encode(self);
+        col_lo.encode(self);
+        len.encode(self);
     }
-}
 
-// copy&paste impl from rustc_metadata
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for Symbol {
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
+    // copy&paste impl from rustc_metadata
+    fn encode_symbol(&mut self, symbol: Symbol) {
         // if symbol preinterned, emit tag and symbol index
-        if self.is_preinterned() {
-            s.encoder.emit_u8(SYMBOL_PREINTERNED);
-            s.encoder.emit_u32(self.as_u32());
+        if symbol.is_preinterned() {
+            self.encoder.emit_u8(SYMBOL_PREINTERNED);
+            self.encoder.emit_u32(symbol.as_u32());
         } else {
             // otherwise write it as string or as offset to it
-            match s.symbol_table.entry(*self) {
+            match self.symbol_table.entry(symbol) {
                 Entry::Vacant(o) => {
-                    s.encoder.emit_u8(SYMBOL_STR);
-                    let pos = s.encoder.position();
+                    self.encoder.emit_u8(SYMBOL_STR);
+                    let pos = self.encoder.position();
                     o.insert(pos);
-                    s.emit_str(self.as_str());
+                    self.emit_str(symbol.as_str());
                 }
                 Entry::Occupied(o) => {
                     let x = *o.get();
-                    s.emit_u8(SYMBOL_OFFSET);
-                    s.emit_usize(x);
+                    self.emit_u8(SYMBOL_OFFSET);
+                    self.emit_usize(x);
                 }
             }
         }
     }
+
+    fn encode_crate_num(&mut self, crate_num: CrateNum) {
+        self.tcx.stable_crate_id(crate_num).encode(self);
+    }
+
+    fn encode_def_id(&mut self, def_id: DefId) {
+        self.tcx.def_path_hash(def_id).encode(self);
+    }
+
+    fn encode_def_index(&mut self, _def_index: DefIndex) {
+        bug!("encoding `DefIndex` without context");
+    }
 }
 
 impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> {
@@ -966,26 +963,6 @@ impl<'a, 'tcx> TyEncoder for CacheEncoder<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for CrateNum {
-    #[inline]
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
-        s.tcx.stable_crate_id(*self).encode(s);
-    }
-}
-
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for DefId {
-    #[inline]
-    fn encode(&self, s: &mut CacheEncoder<'a, 'tcx>) {
-        s.tcx.def_path_hash(*self).encode(s);
-    }
-}
-
-impl<'a, 'tcx> Encodable<CacheEncoder<'a, 'tcx>> for DefIndex {
-    fn encode(&self, _: &mut CacheEncoder<'a, 'tcx>) {
-        bug!("encoding `DefIndex` without context");
-    }
-}
-
 macro_rules! encoder_methods {
     ($($name:ident($ty:ty);)*) => {
         #[inline]
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index d2ff1e3c094..a41d4f1ad58 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -53,7 +53,7 @@ pub struct DynamicQuery<'tcx, C: QueryCache> {
         fn(tcx: TyCtxt<'tcx>, key: &C::Key, index: SerializedDepNodeIndex) -> bool,
     pub hash_result: HashResult<C::Value>,
     pub value_from_cycle_error:
-        fn(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> C::Value,
+        fn(tcx: TyCtxt<'tcx>, cycle_error: &CycleError, guar: ErrorGuaranteed) -> C::Value,
     pub format_value: fn(&C::Value) -> String,
 }
 
@@ -551,7 +551,7 @@ macro_rules! define_feedable {
                                 // We have an inconsistency. This can happen if one of the two
                                 // results is tainted by errors. In this case, delay a bug to
                                 // ensure compilation is doomed, and keep the `old` value.
-                                tcx.dcx().span_delayed_bug(DUMMY_SP, format!(
+                                tcx.dcx().delayed_bug(format!(
                                     "Trying to feed an already recorded value for query {} key={key:?}:\n\
                                     old value: {old:?}\nnew value: {value:?}",
                                     stringify!($name),
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index b6759d35210..b4b8387c262 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -86,8 +86,6 @@ macro_rules! thir_with_elements {
     }
 }
 
-pub const UPVAR_ENV_PARAM: ParamId = ParamId::from_u32(0);
-
 thir_with_elements! {
     body_type: BodyTy<'tcx>,
 
@@ -519,20 +517,13 @@ pub struct FruInfo<'tcx> {
 #[derive(Clone, Debug, HashStable)]
 pub struct Arm<'tcx> {
     pub pattern: Box<Pat<'tcx>>,
-    pub guard: Option<Guard<'tcx>>,
+    pub guard: Option<ExprId>,
     pub body: ExprId,
     pub lint_level: LintLevel,
     pub scope: region::Scope,
     pub span: Span,
 }
 
-/// A `match` guard.
-#[derive(Clone, Debug, HashStable)]
-pub enum Guard<'tcx> {
-    If(ExprId),
-    IfLet(Box<Pat<'tcx>>, ExprId),
-}
-
 #[derive(Copy, Clone, Debug, HashStable)]
 pub enum LogicalOp {
     /// The `&&` operator.
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index ade3ea289cc..4847a7bea91 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -1,5 +1,5 @@
 use super::{
-    AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, Guard, InlineAsmExpr, InlineAsmOperand, Pat,
+    AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
     PatKind, Stmt, StmtKind, Thir,
 };
 
@@ -213,13 +213,8 @@ pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
     visitor: &mut V,
     arm: &'thir Arm<'tcx>,
 ) {
-    match arm.guard {
-        Some(Guard::If(expr)) => visitor.visit_expr(&visitor.thir()[expr]),
-        Some(Guard::IfLet(ref pat, expr)) => {
-            visitor.visit_pat(pat);
-            visitor.visit_expr(&visitor.thir()[expr]);
-        }
-        None => {}
+    if let Some(expr) = arm.guard {
+        visitor.visit_expr(&visitor.thir()[expr])
     }
     visitor.visit_pat(&arm.pattern);
     visitor.visit_expr(&visitor.thir()[arm.body]);
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 09b0a0dfbf3..af601a0d702 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -289,7 +289,7 @@ pub enum ObligationCauseCode<'tcx> {
     /// Type of each variable must be `Sized`.
     VariableType(hir::HirId),
     /// Argument type must be `Sized`.
-    SizedArgumentType(Option<Span>),
+    SizedArgumentType(Option<hir::HirId>),
     /// Return type must be `Sized`.
     SizedReturnType,
     /// Yield type must be `Sized`.
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 2ac3cddfa15..dd41cb5a61f 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -293,12 +293,16 @@ impl<'tcx> InstanceDef<'tcx> {
 fn fmt_instance(
     f: &mut fmt::Formatter<'_>,
     instance: &Instance<'_>,
-    type_length: rustc_session::Limit,
+    type_length: Option<rustc_session::Limit>,
 ) -> fmt::Result {
     ty::tls::with(|tcx| {
         let args = tcx.lift(instance.args).expect("could not lift for printing");
 
-        let mut cx = FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length);
+        let mut cx = if let Some(type_length) = type_length {
+            FmtPrinter::new_with_limit(tcx, Namespace::ValueNS, type_length)
+        } else {
+            FmtPrinter::new(tcx, Namespace::ValueNS)
+        };
         cx.print_def_path(instance.def_id(), args)?;
         let s = cx.into_buffer();
         f.write_str(&s)
@@ -324,13 +328,13 @@ pub struct ShortInstance<'a, 'tcx>(pub &'a Instance<'tcx>, pub usize);
 
 impl<'a, 'tcx> fmt::Display for ShortInstance<'a, 'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt_instance(f, self.0, rustc_session::Limit(self.1))
+        fmt_instance(f, self.0, Some(rustc_session::Limit(self.1)))
     }
 }
 
 impl<'tcx> fmt::Display for Instance<'tcx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        ty::tls::with(|tcx| fmt_instance(f, self, tcx.type_length_limit()))
+        fmt_instance(f, self, None)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index 5cc0ce87c9b..25473f52c03 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -284,7 +284,7 @@ impl<'tcx> LayoutCalculator for LayoutCx<'tcx, TyCtxt<'tcx>> {
     type TargetDataLayoutRef = &'tcx TargetDataLayout;
 
     fn delayed_bug(&self, txt: String) {
-        self.tcx.dcx().span_delayed_bug(DUMMY_SP, txt);
+        self.tcx.dcx().delayed_bug(txt);
     }
 
     fn current_data_layout(&self) -> Self::TargetDataLayoutRef {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 757d3337afc..9e8d7c2ef3e 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -38,6 +38,7 @@ use rustc_data_structures::intern::Interned;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
+use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
@@ -50,7 +51,7 @@ use rustc_session::lint::LintBuffer;
 pub use rustc_session::lint::RegisteredTools;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{ExpnId, ExpnKind, Span};
+use rustc_span::{hygiene, ExpnId, ExpnKind, Span};
 use rustc_target::abi::{Align, FieldIdx, Integer, IntegerType, VariantIdx};
 pub use rustc_target::abi::{ReprFlags, ReprOptions};
 pub use rustc_type_ir::{DebugWithInfcx, InferCtxtLike, WithInfcx};
@@ -655,7 +656,7 @@ pub struct CratePredicatesMap<'tcx> {
     /// For each struct with outlive bounds, maps to a vector of the
     /// predicate of its outlive bounds. If an item has no outlives
     /// bounds, it will have no entry.
-    pub predicates: FxHashMap<DefId, &'tcx [(Clause<'tcx>, Span)]>,
+    pub predicates: DefIdMap<&'tcx [(Clause<'tcx>, Span)]>,
 }
 
 impl<'tcx> Clause<'tcx> {
@@ -2517,21 +2518,21 @@ impl<'tcx> TyCtxt<'tcx> {
         (ident, scope)
     }
 
-    /// Returns `true` if the debuginfo for `span` should be collapsed to the outermost expansion
-    /// site. Only applies when `Span` is the result of macro expansion.
+    /// Returns corrected span if the debuginfo for `span` should be collapsed to the outermost
+    /// expansion site (with collapse_debuginfo attribute if the corresponding feature enabled).
+    /// Only applies when `Span` is the result of macro expansion.
     ///
     /// - If the `collapse_debuginfo` feature is enabled then debuginfo is not collapsed by default
-    ///   and only when a macro definition is annotated with `#[collapse_debuginfo]`.
+    ///   and only when a (some enclosing) macro definition is annotated with `#[collapse_debuginfo]`.
     /// - If `collapse_debuginfo` is not enabled, then debuginfo is collapsed by default.
     ///
     /// When `-Zdebug-macros` is provided then debuginfo will never be collapsed.
-    pub fn should_collapse_debuginfo(self, span: Span) -> bool {
-        !self.sess.opts.unstable_opts.debug_macros
-            && if self.features().collapse_debuginfo {
-                span.in_macro_expansion_with_collapse_debuginfo()
-            } else {
-                span.from_expansion()
-            }
+    pub fn collapsed_debuginfo(self, span: Span, upto: Span) -> Span {
+        if self.sess.opts.unstable_opts.debug_macros || !span.from_expansion() {
+            return span;
+        }
+        let collapse_debuginfo_enabled = self.features().collapse_debuginfo;
+        hygiene::walk_chain_collapsed(span, upto, collapse_debuginfo_enabled)
     }
 
     #[inline]
@@ -2658,7 +2659,7 @@ pub fn provide(providers: &mut Providers) {
 #[derive(Clone, Debug, Default, HashStable)]
 pub struct CrateInherentImpls {
     pub inherent_impls: LocalDefIdMap<Vec<DefId>>,
-    pub incoherent_impls: FxHashMap<SimplifiedType, Vec<LocalDefId>>,
+    pub incoherent_impls: UnordMap<SimplifiedType, Vec<LocalDefId>>,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, TyEncodable, HashStable)]
diff --git a/compiler/rustc_middle/src/ty/opaque_types.rs b/compiler/rustc_middle/src/ty/opaque_types.rs
index fc4d4c9a3d2..5ca88ec3102 100644
--- a/compiler/rustc_middle/src/ty/opaque_types.rs
+++ b/compiler/rustc_middle/src/ty/opaque_types.rs
@@ -132,7 +132,7 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReverseMapper<'tcx> {
                     .tcx
                     .dcx()
                     .struct_span_err(self.span, "non-defining opaque type use in defining scope")
-                    .span_label(
+                    .with_span_label(
                         self.span,
                         format!(
                             "lifetime `{r}` is part of concrete type but not used in \
diff --git a/compiler/rustc_middle/src/ty/parameterized.rs b/compiler/rustc_middle/src/ty/parameterized.rs
index a63a4eff5e1..47f9d9e61ad 100644
--- a/compiler/rustc_middle/src/ty/parameterized.rs
+++ b/compiler/rustc_middle/src/ty/parameterized.rs
@@ -1,6 +1,7 @@
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordMap;
 use rustc_hir::def_id::DefIndex;
 use rustc_index::{Idx, IndexVec};
+use std::hash::Hash;
 
 use crate::ty;
 
@@ -24,8 +25,8 @@ impl<I: Idx + 'static, T: ParameterizedOverTcx> ParameterizedOverTcx for IndexVe
     type Value<'tcx> = IndexVec<I, T::Value<'tcx>>;
 }
 
-impl<I: 'static, T: ParameterizedOverTcx> ParameterizedOverTcx for FxHashMap<I, T> {
-    type Value<'tcx> = FxHashMap<I, T::Value<'tcx>>;
+impl<I: Hash + Eq + 'static, T: ParameterizedOverTcx> ParameterizedOverTcx for UnordMap<I, T> {
+    type Value<'tcx> = UnordMap<I, T::Value<'tcx>>;
 }
 
 impl<T: ParameterizedOverTcx> ParameterizedOverTcx for ty::Binder<'static, T> {
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index ebbd02e01bf..a10bdc6012c 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -12,7 +12,7 @@ use rustc_apfloat::Float;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, DefKind, Namespace};
-use rustc_hir::def_id::{DefIdSet, ModDefId, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::def_id::{DefIdMap, DefIdSet, ModDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::definitions::{DefKey, DefPathDataName};
 use rustc_hir::LangItem;
 use rustc_session::config::TrimmedDefPaths;
@@ -912,7 +912,8 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
 
         let mut traits = FxIndexMap::default();
         let mut fn_traits = FxIndexMap::default();
-        let mut is_sized = false;
+        let mut has_sized_bound = false;
+        let mut has_negative_sized_bound = false;
         let mut lifetimes = SmallVec::<[ty::Region<'tcx>; 1]>::new();
 
         for (predicate, _) in bounds.iter_instantiated_copied(tcx, args) {
@@ -922,13 +923,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 ty::ClauseKind::Trait(pred) => {
                     let trait_ref = bound_predicate.rebind(pred.trait_ref);
 
-                    // Don't print + Sized, but rather + ?Sized if absent.
+                    // Don't print `+ Sized`, but rather `+ ?Sized` if absent.
                     if Some(trait_ref.def_id()) == tcx.lang_items().sized_trait() {
-                        is_sized = true;
-                        continue;
+                        match pred.polarity {
+                            ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => {
+                                has_sized_bound = true;
+                                continue;
+                            }
+                            ty::ImplPolarity::Negative => has_negative_sized_bound = true,
+                        }
                     }
 
-                    self.insert_trait_and_projection(trait_ref, None, &mut traits, &mut fn_traits);
+                    self.insert_trait_and_projection(
+                        trait_ref,
+                        pred.polarity,
+                        None,
+                        &mut traits,
+                        &mut fn_traits,
+                    );
                 }
                 ty::ClauseKind::Projection(pred) => {
                     let proj_ref = bound_predicate.rebind(pred);
@@ -939,6 +951,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
 
                     self.insert_trait_and_projection(
                         trait_ref,
+                        ty::ImplPolarity::Positive,
                         Some(proj_ty),
                         &mut traits,
                         &mut fn_traits,
@@ -955,7 +968,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
 
         let mut first = true;
         // Insert parenthesis around (Fn(A, B) -> C) if the opaque ty has more than one other trait
-        let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !is_sized;
+        let paren_needed = fn_traits.len() > 1 || traits.len() > 0 || !has_sized_bound;
 
         for (fn_once_trait_ref, entry) in fn_traits {
             write!(self, "{}", if first { "" } else { " + " })?;
@@ -1002,18 +1015,21 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     // trait_refs we collected in the OpaqueFnEntry as normal trait refs.
                     _ => {
                         if entry.has_fn_once {
-                            traits.entry(fn_once_trait_ref).or_default().extend(
-                                // Group the return ty with its def id, if we had one.
-                                entry
-                                    .return_ty
-                                    .map(|ty| (tcx.require_lang_item(LangItem::FnOnce, None), ty)),
-                            );
+                            traits
+                                .entry((fn_once_trait_ref, ty::ImplPolarity::Positive))
+                                .or_default()
+                                .extend(
+                                    // Group the return ty with its def id, if we had one.
+                                    entry.return_ty.map(|ty| {
+                                        (tcx.require_lang_item(LangItem::FnOnce, None), ty)
+                                    }),
+                                );
                         }
                         if let Some(trait_ref) = entry.fn_mut_trait_ref {
-                            traits.entry(trait_ref).or_default();
+                            traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
                         }
                         if let Some(trait_ref) = entry.fn_trait_ref {
-                            traits.entry(trait_ref).or_default();
+                            traits.entry((trait_ref, ty::ImplPolarity::Positive)).or_default();
                         }
                     }
                 }
@@ -1023,11 +1039,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         }
 
         // Print the rest of the trait types (that aren't Fn* family of traits)
-        for (trait_ref, assoc_items) in traits {
+        for ((trait_ref, polarity), assoc_items) in traits {
             write!(self, "{}", if first { "" } else { " + " })?;
 
             self.wrap_binder(&trait_ref, |trait_ref, cx| {
                 define_scoped_cx!(cx);
+
+                if polarity == ty::ImplPolarity::Negative {
+                    p!("!");
+                }
                 p!(print(trait_ref.print_only_trait_name()));
 
                 let generics = tcx.generics_of(trait_ref.def_id);
@@ -1094,9 +1114,15 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             })?;
         }
 
-        if !is_sized {
-            write!(self, "{}?Sized", if first { "" } else { " + " })?;
-        } else if first {
+        let add_sized = has_sized_bound && (first || has_negative_sized_bound);
+        let add_maybe_sized = !has_sized_bound && !has_negative_sized_bound;
+        if add_sized || add_maybe_sized {
+            if !first {
+                write!(self, " + ")?;
+            }
+            if add_maybe_sized {
+                write!(self, "?")?;
+            }
             write!(self, "Sized")?;
         }
 
@@ -1128,9 +1154,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
     fn insert_trait_and_projection(
         &mut self,
         trait_ref: ty::PolyTraitRef<'tcx>,
+        polarity: ty::ImplPolarity,
         proj_ty: Option<(DefId, ty::Binder<'tcx, Term<'tcx>>)>,
         traits: &mut FxIndexMap<
-            ty::PolyTraitRef<'tcx>,
+            (ty::PolyTraitRef<'tcx>, ty::ImplPolarity),
             FxIndexMap<DefId, ty::Binder<'tcx, Term<'tcx>>>,
         >,
         fn_traits: &mut FxIndexMap<ty::PolyTraitRef<'tcx>, OpaqueFnEntry<'tcx>>,
@@ -1139,7 +1166,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
 
         // If our trait_ref is FnOnce or any of its children, project it onto the parent FnOnce
         // super-trait ref and record it there.
-        if let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait() {
+        // We skip negative Fn* bounds since they can't use parenthetical notation anyway.
+        if polarity == ty::ImplPolarity::Positive
+            && let Some(fn_once_trait) = self.tcx().lang_items().fn_once_trait()
+        {
             // If we have a FnOnce, then insert it into
             if trait_def_id == fn_once_trait {
                 let entry = fn_traits.entry(trait_ref).or_default();
@@ -1167,7 +1197,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
         }
 
         // Otherwise, just group our traits and projection types.
-        traits.entry(trait_ref).or_default().extend(proj_ty);
+        traits.entry((trait_ref, polarity)).or_default().extend(proj_ty);
     }
 
     fn pretty_print_inherent_projection(
@@ -3049,8 +3079,8 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
 ///
 /// See also [`DelayDm`](rustc_error_messages::DelayDm) and [`with_no_trimmed_paths!`].
 // this is pub to be able to intra-doc-link it
-pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> FxHashMap<DefId, Symbol> {
-    let mut map: FxHashMap<DefId, Symbol> = FxHashMap::default();
+pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> {
+    let mut map: DefIdMap<Symbol> = Default::default();
 
     if let TrimmedDefPaths::GoodPath = tcx.sess.opts.trimmed_def_paths {
         // Trimming paths is expensive and not optimized, since we expect it to only be used for error reporting.
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 38bf39bff90..8cf5fc8013f 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -1225,7 +1225,7 @@ impl<'tcx> AliasTy<'tcx> {
 
     /// Whether this alias type is an opaque.
     pub fn is_opaque(self, tcx: TyCtxt<'tcx>) -> bool {
-        matches!(self.opt_kind(tcx), Some(ty::AliasKind::Opaque))
+        matches!(self.opt_kind(tcx), Some(ty::Opaque))
     }
 
     /// FIXME: rename `AliasTy` to `AliasTerm` and always handle
@@ -2745,7 +2745,7 @@ impl<'tcx> Ty<'tcx> {
             // Extern types have metadata = ().
             | ty::Foreign(..)
             // `dyn*` has no metadata
-            | ty::Dynamic(_, _, DynKind::DynStar)
+            | ty::Dynamic(_, _, ty::DynStar)
             // If returned by `struct_tail_without_normalization` this is a unit struct
             // without any fields, or not a struct, and therefore is Sized.
             | ty::Adt(..)
@@ -2754,7 +2754,7 @@ impl<'tcx> Ty<'tcx> {
             | ty::Tuple(..) => (tcx.types.unit, false),
 
             ty::Str | ty::Slice(_) => (tcx.types.usize, false),
-            ty::Dynamic(_, _, DynKind::Dyn) => {
+            ty::Dynamic(_, _, ty::Dyn) => {
                 let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
                 (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
             },
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index 58699c934b6..ad41a674dd8 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -527,7 +527,7 @@ impl<'a, V> LocalTableInContext<'a, V> {
     }
 
     pub fn items_in_stable_order(&self) -> Vec<(ItemLocalId, &'a V)> {
-        self.data.to_sorted_stable_ord()
+        self.data.items().map(|(&k, v)| (k, v)).into_sorted_stable_ord_by_key(|(k, _)| k)
     }
 }
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index ad2442a7963..372f11a5acc 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1,7 +1,7 @@
 //! Miscellaneous type-system utilities that are too small to deserve their own modules.
 
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use crate::query::Providers;
+use crate::query::{IntoQueryParam, Providers};
 use crate::ty::layout::IntegerExt;
 use crate::ty::{
     self, FallibleTypeFolder, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
@@ -369,7 +369,7 @@ impl<'tcx> TyCtxt<'tcx> {
             if let Some((old_item_id, _)) = dtor_candidate {
                 self.dcx()
                     .struct_span_err(self.def_span(item_id), "multiple drop impls found")
-                    .span_note(self.def_span(old_item_id), "other impl here")
+                    .with_span_note(self.def_span(old_item_id), "other impl here")
                     .delay_as_bug();
             }
 
@@ -702,6 +702,7 @@ impl<'tcx> TyCtxt<'tcx> {
         self,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
+        inspect_coroutine_fields: InspectCoroutineFields,
     ) -> Result<Ty<'tcx>, Ty<'tcx>> {
         let mut visitor = OpaqueTypeExpander {
             seen_opaque_tys: FxHashSet::default(),
@@ -712,6 +713,7 @@ impl<'tcx> TyCtxt<'tcx> {
             check_recursion: true,
             expand_coroutines: true,
             tcx: self,
+            inspect_coroutine_fields,
         };
 
         let expanded_type = visitor.expand_opaque_ty(def_id, args).unwrap();
@@ -729,16 +731,43 @@ impl<'tcx> TyCtxt<'tcx> {
             DefKind::AssocFn if self.associated_item(def_id).fn_has_self_parameter => "method",
             DefKind::Closure if let Some(coroutine_kind) = self.coroutine_kind(def_id) => {
                 match coroutine_kind {
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _) => {
-                        "async closure"
-                    }
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
-                        "async gen closure"
-                    }
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Fn,
+                    ) => "async fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Block,
+                    ) => "async block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Async,
+                        hir::CoroutineSource::Closure,
+                    ) => "async closure",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Fn,
+                    ) => "async gen fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Block,
+                    ) => "async gen block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::AsyncGen,
+                        hir::CoroutineSource::Closure,
+                    ) => "async gen closure",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Fn,
+                    ) => "gen fn",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Block,
+                    ) => "gen block",
+                    hir::CoroutineKind::Desugared(
+                        hir::CoroutineDesugaring::Gen,
+                        hir::CoroutineSource::Closure,
+                    ) => "gen closure",
                     hir::CoroutineKind::Coroutine(_) => "coroutine",
-                    hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
-                        "gen closure"
-                    }
                 }
             }
             _ => def_kind.descr(def_id),
@@ -786,6 +815,13 @@ impl<'tcx> TyCtxt<'tcx> {
             || self.extern_crate(key.as_def_id()).is_some_and(|e| e.is_direct())
     }
 
+    /// Whether the item has a host effect param. This is different from `TyCtxt::is_const`,
+    /// because the item must also be "maybe const", and the crate where the item is
+    /// defined must also have the effects feature enabled.
+    pub fn has_host_param(self, def_id: impl IntoQueryParam<DefId>) -> bool {
+        self.generics_of(def_id).host_effect_index.is_some()
+    }
+
     pub fn expected_host_effect_param_for_body(self, def_id: impl Into<DefId>) -> ty::Const<'tcx> {
         let def_id = def_id.into();
         // FIXME(effects): This is suspicious and should probably not be done,
@@ -858,6 +894,13 @@ struct OpaqueTypeExpander<'tcx> {
     /// recursion, and 'false' otherwise to avoid unnecessary work.
     check_recursion: bool,
     tcx: TyCtxt<'tcx>,
+    inspect_coroutine_fields: InspectCoroutineFields,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum InspectCoroutineFields {
+    No,
+    Yes,
 }
 
 impl<'tcx> OpaqueTypeExpander<'tcx> {
@@ -899,9 +942,11 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
             let expanded_ty = match self.expanded_cache.get(&(def_id, args)) {
                 Some(expanded_ty) => *expanded_ty,
                 None => {
-                    for bty in self.tcx.coroutine_hidden_types(def_id) {
-                        let hidden_ty = bty.instantiate(self.tcx, args);
-                        self.fold_ty(hidden_ty);
+                    if matches!(self.inspect_coroutine_fields, InspectCoroutineFields::Yes) {
+                        for bty in self.tcx.coroutine_hidden_types(def_id) {
+                            let hidden_ty = bty.instantiate(self.tcx, args);
+                            self.fold_ty(hidden_ty);
+                        }
                     }
                     let expanded_ty = Ty::new_coroutine_witness(self.tcx, def_id, args);
                     self.expanded_cache.insert((def_id, args), expanded_ty);
@@ -1479,11 +1524,12 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
         check_recursion: false,
         expand_coroutines: false,
         tcx,
+        inspect_coroutine_fields: InspectCoroutineFields::No,
     };
     val.fold_with(&mut visitor)
 }
 
-/// Determines whether an item is annotated with `doc(hidden)`.
+/// Determines whether an item is directly annotated with `doc(hidden)`.
 fn is_doc_hidden(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
     tcx.get_attrs(def_id, sym::doc)
         .filter_map(|attr| attr.meta_item_list())
diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs
index b4e45ad5685..6a03bf243eb 100644
--- a/compiler/rustc_middle/src/values.rs
+++ b/compiler/rustc_middle/src/values.rs
@@ -1,20 +1,22 @@
 use crate::dep_graph::dep_kinds;
 use crate::query::plumbing::CyclePlaceholder;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_middle::ty::Representability;
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_query_system::query::QueryInfo;
+use rustc_query_system::query::{report_cycle, CycleError};
 use rustc_query_system::Value;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{ErrorGuaranteed, Span};
 
+use std::collections::VecDeque;
 use std::fmt::Write;
+use std::ops::ControlFlow;
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
         // SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe { std::mem::transmute::<Ty<'tcx>, Ty<'_>>(Ty::new_error(tcx, guar)) }
@@ -22,13 +24,13 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Ty<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Result<ty::EarlyBinder<Ty<'_>>, CyclePlaceholder> {
-    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _: &CycleError, guar: ErrorGuaranteed) -> Self {
         Err(CyclePlaceholder(guar))
     }
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &[QueryInfo], _guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(tcx: TyCtxt<'tcx>, _: &CycleError, _guar: ErrorGuaranteed) -> Self {
         // SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
         // FIXME: Represent the above fact in the trait system somehow.
         unsafe {
@@ -40,10 +42,14 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, stack: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
         let err = Ty::new_error(tcx, guar);
 
-        let arity = if let Some(frame) = stack.get(0)
+        let arity = if let Some(frame) = cycle_error.cycle.get(0)
             && frame.query.dep_kind == dep_kinds::fn_sig
             && let Some(def_id) = frame.query.def_id
             && let Some(node) = tcx.hir().get_if_local(def_id)
@@ -70,10 +76,14 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        _guar: ErrorGuaranteed,
+    ) -> Self {
         let mut item_and_field_ids = Vec::new();
         let mut representable_ids = FxHashSet::default();
-        for info in cycle {
+        for info in &cycle_error.cycle {
             if info.query.dep_kind == dep_kinds::representability
                 && let Some(field_id) = info.query.def_id
                 && let Some(field_id) = field_id.as_local()
@@ -87,9 +97,9 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
                 item_and_field_ids.push((item_id.expect_local(), field_id));
             }
         }
-        for info in cycle {
+        for info in &cycle_error.cycle {
             if info.query.dep_kind == dep_kinds::representability_adt_ty
-                && let Some(def_id) = info.query.ty_adt_id
+                && let Some(def_id) = info.query.ty_def_id
                 && let Some(def_id) = def_id.as_local()
                 && !item_and_field_ids.iter().any(|&(id, _)| id == def_id)
             {
@@ -102,19 +112,128 @@ impl<'tcx> Value<TyCtxt<'tcx>> for Representability {
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<Ty<'_>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
-        ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle, guar))
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
+        ty::EarlyBinder::bind(Ty::from_cycle_error(tcx, cycle_error, guar))
     }
 }
 
 impl<'tcx> Value<TyCtxt<'tcx>> for ty::EarlyBinder<ty::Binder<'_, ty::FnSig<'_>>> {
-    fn from_cycle_error(tcx: TyCtxt<'tcx>, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
-        ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle, guar))
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        guar: ErrorGuaranteed,
+    ) -> Self {
+        ty::EarlyBinder::bind(ty::Binder::from_cycle_error(tcx, cycle_error, guar))
     }
 }
 
+// Take a cycle of `Q` and try `try_cycle` on every permutation, falling back to `otherwise`.
+fn search_for_cycle_permutation<Q, T>(
+    cycle: &[Q],
+    try_cycle: impl Fn(&mut VecDeque<&Q>) -> ControlFlow<T, ()>,
+    otherwise: impl FnOnce() -> T,
+) -> T {
+    let mut cycle: VecDeque<_> = cycle.iter().collect();
+    for _ in 0..cycle.len() {
+        match try_cycle(&mut cycle) {
+            ControlFlow::Continue(_) => {
+                cycle.rotate_left(1);
+            }
+            ControlFlow::Break(t) => return t,
+        }
+    }
+
+    otherwise()
+}
+
 impl<'tcx, T> Value<TyCtxt<'tcx>> for Result<T, &'_ ty::layout::LayoutError<'_>> {
-    fn from_cycle_error(_tcx: TyCtxt<'tcx>, _cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self {
+    fn from_cycle_error(
+        tcx: TyCtxt<'tcx>,
+        cycle_error: &CycleError,
+        _guar: ErrorGuaranteed,
+    ) -> Self {
+        let diag = search_for_cycle_permutation(
+            &cycle_error.cycle,
+            |cycle| {
+                if cycle[0].query.dep_kind == dep_kinds::layout_of
+                    && let Some(def_id) = cycle[0].query.ty_def_id
+                    && let Some(def_id) = def_id.as_local()
+                    && let def_kind = tcx.def_kind(def_id)
+                    && matches!(def_kind, DefKind::Closure)
+                    && let Some(coroutine_kind) = tcx.coroutine_kind(def_id)
+                {
+                    // FIXME: `def_span` for an fn-like coroutine will point to the fn's body
+                    // due to interactions between the desugaring into a closure expr and the
+                    // def_span code. I'm not motivated to fix it, because I tried and it was
+                    // not working, so just hack around it by grabbing the parent fn's span.
+                    let span = if coroutine_kind.is_fn_like() {
+                        tcx.def_span(tcx.local_parent(def_id))
+                    } else {
+                        tcx.def_span(def_id)
+                    };
+                    let mut diag = struct_span_code_err!(
+                        tcx.sess.dcx(),
+                        span,
+                        E0733,
+                        "recursion in {} {} requires boxing",
+                        tcx.def_kind_descr_article(def_kind, def_id.to_def_id()),
+                        tcx.def_kind_descr(def_kind, def_id.to_def_id()),
+                    );
+                    for (i, frame) in cycle.iter().enumerate() {
+                        if frame.query.dep_kind != dep_kinds::layout_of {
+                            continue;
+                        }
+                        let Some(frame_def_id) = frame.query.ty_def_id else {
+                            continue;
+                        };
+                        let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else {
+                            continue;
+                        };
+                        let frame_span =
+                            frame.query.default_span(cycle[(i + 1) % cycle.len()].span);
+                        if frame_span.is_dummy() {
+                            continue;
+                        }
+                        if i == 0 {
+                            diag.span_label(frame_span, "recursive call here");
+                        } else {
+                            let coroutine_span: Span = if frame_coroutine_kind.is_fn_like() {
+                                tcx.def_span(tcx.parent(frame_def_id))
+                            } else {
+                                tcx.def_span(frame_def_id)
+                            };
+                            let mut multispan = MultiSpan::from_span(coroutine_span);
+                            multispan
+                                .push_span_label(frame_span, "...leading to this recursive call");
+                            diag.span_note(
+                                multispan,
+                                format!("which leads to this {}", tcx.def_descr(frame_def_id)),
+                            );
+                        }
+                    }
+                    // FIXME: We could report a structured suggestion if we had
+                    // enough info here... Maybe we can use a hacky HIR walker.
+                    if matches!(
+                        coroutine_kind,
+                        hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)
+                    ) {
+                        diag.note("a recursive `async fn` call must introduce indirection such as `Box::pin` to avoid an infinitely sized future");
+                    }
+
+                    ControlFlow::Break(diag)
+                } else {
+                    ControlFlow::Continue(())
+                }
+            },
+            || report_cycle(tcx.sess, cycle_error),
+        );
+
+        let guar = diag.emit();
+
         // tcx.arena.alloc cannot be used because we are not allowed to use &'tcx LayoutError under
         // min_specialization. Since this is an error path anyways, leaking doesn't matter (and really,
         // tcx.arena.alloc is pretty much equal to leaking).
@@ -190,7 +309,7 @@ pub fn recursive_type_error(
         }
         s
     };
-    let mut err = struct_span_err!(
+    struct_span_code_err!(
         tcx.dcx(),
         err_span,
         E0072,
@@ -198,13 +317,13 @@ pub fn recursive_type_error(
         pluralize!(cycle_len),
         items_list,
         pluralize!("has", cycle_len),
-    );
-    err.multipart_suggestion(
+    )
+    .with_multipart_suggestion(
         "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle",
         suggestion,
         Applicability::HasPlaceholders,
-    );
-    err.emit();
+    )
+    .emit();
 }
 
 fn find_item_ty_spans(
diff --git a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
index 4ce7f831c87..e3dea2212df 100644
--- a/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
+++ b/compiler/rustc_mir_build/src/build/custom/parse/instruction.rs
@@ -61,7 +61,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             @call(mir_drop, args) => {
                 Ok(TerminatorKind::Drop {
                     place: self.parse_place(args[0])?,
-                    target: self.parse_block(args[1])?,
+                    target: self.parse_return_to(args[1])?,
                     unwind: self.parse_unwind_action(args[2])?,
                     replace: false,
                 })
@@ -104,6 +104,14 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
         )
     }
 
+    fn parse_return_to(&self, expr_id: ExprId) -> PResult<BasicBlock> {
+        parse_by_kind!(self, expr_id, _, "return block",
+            @call(mir_return_to, args) => {
+                self.parse_block(args[0])
+            },
+        )
+    }
+
     fn parse_match(&self, arms: &[ArmId], span: Span) -> PResult<SwitchTargets> {
         let Some((otherwise, rest)) = arms.split_last() else {
             return Err(ParseError {
@@ -146,7 +154,7 @@ impl<'tcx, 'body> ParseCtxt<'tcx, 'body> {
             ExprKind::Assign { lhs, rhs } => (*lhs, *rhs),
         );
         let destination = self.parse_place(destination)?;
-        let target = self.parse_block(args[1])?;
+        let target = self.parse_return_to(args[1])?;
         let unwind = self.parse_unwind_action(args[2])?;
 
         parse_by_kind!(self, call, _, "function call",
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index e77e82d9954..5721957037e 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -9,7 +9,6 @@ use rustc_middle::thir::*;
 use rustc_middle::ty::{
     self, CanonicalUserType, CanonicalUserTypeAnnotation, TyCtxt, UserTypeAnnotationIndex,
 };
-use rustc_span::DUMMY_SP;
 use rustc_target::abi::Size;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -111,15 +110,15 @@ fn lit_to_mir_constant<'tcx>(
     let LitToConstInput { lit, ty, neg } = lit_input;
     let trunc = |n| {
         let param_ty = ty::ParamEnv::reveal_all().and(ty);
-        let width = tcx
-            .layout_of(param_ty)
-            .map_err(|_| {
-                LitToConstError::Reported(tcx.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("couldn't compute width of literal: {:?}", lit_input.lit),
-                ))
-            })?
-            .size;
+        let width =
+            tcx.layout_of(param_ty)
+                .map_err(|_| {
+                    LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
+                        "couldn't compute width of literal: {:?}",
+                        lit_input.lit
+                    )))
+                })?
+                .size;
         trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
         let result = width.truncate(n);
         trace!("trunc result: {}", result);
@@ -158,16 +157,16 @@ fn lit_to_mir_constant<'tcx>(
         }
         (ast::LitKind::Float(n, _), ty::Float(fty)) => parse_float_into_constval(*n, *fty, neg)
             .ok_or_else(|| {
-                LitToConstError::Reported(tcx.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("couldn't parse float literal: {:?}", lit_input.lit),
-                ))
+                LitToConstError::Reported(
+                    tcx.dcx()
+                        .delayed_bug(format!("couldn't parse float literal: {:?}", lit_input.lit)),
+                )
             })?,
         (ast::LitKind::Bool(b), ty::Bool) => ConstValue::Scalar(Scalar::from_bool(*b)),
         (ast::LitKind::Char(c), ty::Char) => ConstValue::Scalar(Scalar::from_char(*c)),
         (ast::LitKind::Err, _) => {
             return Err(LitToConstError::Reported(
-                tcx.dcx().span_delayed_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
+                tcx.dcx().delayed_bug("encountered LitKind::Err during mir build"),
             ));
         }
         _ => return Err(LitToConstError::TypeError),
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index f50945a4de0..060a3b521a4 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -82,7 +82,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                         cond,
                                         Some(condition_scope),
                                         condition_scope,
-                                        source_info
+                                        source_info,
+                                        true,
                                     ));
 
                                     this.expr_into_dest(destination, then_blk, then)
@@ -173,6 +174,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             Some(condition_scope),
                             condition_scope,
                             source_info,
+                            true,
                         )
                     });
                 let (short_circuit, continuation, constant) = match op {
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 7f29e3308f4..906b3205ca7 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -33,6 +33,12 @@ use std::borrow::Borrow;
 use std::mem;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
+    /// Lowers a condition in a way that ensures that variables bound in any let
+    /// expressions are definitely initialized in the if body.
+    ///
+    /// If `declare_bindings` is false then variables created in `let`
+    /// expressions will not be declared. This is for if let guards on arms with
+    /// an or pattern, where the guard is lowered multiple times.
     pub(crate) fn then_else_break(
         &mut self,
         mut block: BasicBlock,
@@ -40,6 +46,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         temp_scope_override: Option<region::Scope>,
         break_scope: region::Scope,
         variable_source_info: SourceInfo,
+        declare_bindings: bool,
     ) -> BlockAnd<()> {
         let this = self;
         let expr = &this.thir[expr_id];
@@ -53,6 +60,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     temp_scope_override,
                     break_scope,
                     variable_source_info,
+                    declare_bindings,
                 ));
 
                 let rhs_then_block = unpack!(this.then_else_break(
@@ -61,6 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     temp_scope_override,
                     break_scope,
                     variable_source_info,
+                    declare_bindings,
                 ));
 
                 rhs_then_block.unit()
@@ -75,6 +84,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             temp_scope_override,
                             local_scope,
                             variable_source_info,
+                            true,
                         )
                     });
                 let rhs_success_block = unpack!(this.then_else_break(
@@ -83,6 +93,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     temp_scope_override,
                     break_scope,
                     variable_source_info,
+                    true,
                 ));
                 this.cfg.goto(lhs_success_block, variable_source_info, rhs_success_block);
                 rhs_success_block.unit()
@@ -102,6 +113,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             temp_scope_override,
                             local_scope,
                             variable_source_info,
+                            true,
                         )
                     });
                 this.break_for_else(success_block, break_scope, variable_source_info);
@@ -116,6 +128,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         temp_scope_override,
                         break_scope,
                         variable_source_info,
+                        declare_bindings,
                     )
                 })
             }
@@ -125,6 +138,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 temp_scope_override,
                 break_scope,
                 variable_source_info,
+                declare_bindings,
             ),
             ExprKind::Let { expr, ref pat } => this.lower_let_expr(
                 block,
@@ -133,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 break_scope,
                 Some(variable_source_info.scope),
                 variable_source_info.span,
-                true,
+                declare_bindings,
             ),
             _ => {
                 let temp_scope = temp_scope_override.unwrap_or_else(|| this.local_scope());
@@ -417,7 +431,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         None,
                         arm.span,
                         &arm.pattern,
-                        arm.guard.as_ref(),
+                        arm.guard,
                         opt_scrutinee_place,
                     );
 
@@ -709,7 +723,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         mut visibility_scope: Option<SourceScope>,
         scope_span: Span,
         pattern: &Pat<'tcx>,
-        guard: Option<&Guard<'tcx>>,
+        guard: Option<ExprId>,
         opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
     ) -> Option<SourceScope> {
         self.visit_primary_bindings(
@@ -737,13 +751,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 );
             },
         );
-        if let Some(Guard::IfLet(guard_pat, _)) = guard {
-            // FIXME: pass a proper `opt_match_place`
-            self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);
+        if let Some(guard_expr) = guard {
+            self.declare_guard_bindings(guard_expr, scope_span, visibility_scope);
         }
         visibility_scope
     }
 
+    /// Declare bindings in a guard. This has to be done when declaring bindings
+    /// for an arm to ensure that or patterns only have one version of each
+    /// variable.
+    pub(crate) fn declare_guard_bindings(
+        &mut self,
+        guard_expr: ExprId,
+        scope_span: Span,
+        visibility_scope: Option<SourceScope>,
+    ) {
+        match self.thir.exprs[guard_expr].kind {
+            ExprKind::Let { expr: _, pat: ref guard_pat } => {
+                // FIXME: pass a proper `opt_match_place`
+                self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);
+            }
+            ExprKind::Scope { value, .. } => {
+                self.declare_guard_bindings(value, scope_span, visibility_scope);
+            }
+            ExprKind::Use { source } => {
+                self.declare_guard_bindings(source, scope_span, visibility_scope);
+            }
+            ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
+                self.declare_guard_bindings(lhs, scope_span, visibility_scope);
+                self.declare_guard_bindings(rhs, scope_span, visibility_scope);
+            }
+            _ => {}
+        }
+    }
+
     pub(crate) fn storage_live_binding(
         &mut self,
         block: BasicBlock,
@@ -2009,7 +2050,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         //    * So we eagerly create the reference for the arm and then take a
         //      reference to that.
         if let Some((arm, match_scope)) = arm_match_scope
-            && let Some(guard) = &arm.guard
+            && let Some(guard) = arm.guard
         {
             let tcx = self.tcx;
             let bindings = parent_bindings
@@ -2034,21 +2075,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             let mut guard_span = rustc_span::DUMMY_SP;
 
             let (post_guard_block, otherwise_post_guard_block) =
-                self.in_if_then_scope(match_scope, guard_span, |this| match *guard {
-                    Guard::If(e) => {
-                        guard_span = this.thir[e].span;
-                        this.then_else_break(
-                            block,
-                            e,
-                            None,
-                            match_scope,
-                            this.source_info(arm.span),
-                        )
-                    }
-                    Guard::IfLet(ref pat, s) => {
-                        guard_span = this.thir[s].span;
-                        this.lower_let_expr(block, s, pat, match_scope, None, arm.span, false)
-                    }
+                self.in_if_then_scope(match_scope, guard_span, |this| {
+                    guard_span = this.thir[guard].span;
+                    this.then_else_break(
+                        block,
+                        guard,
+                        None,
+                        match_scope,
+                        this.source_info(arm.span),
+                        false,
+                    )
                 });
 
             let source_info = self.source_info(guard_span);
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index e0199fb8767..b8d08319422 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -9,7 +9,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_hir::{CoroutineKind, Node};
+use rustc_hir::Node;
 use rustc_index::bit_set::GrowableBitSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@@ -177,7 +177,7 @@ struct Builder<'a, 'tcx> {
     check_overflow: bool,
     fn_span: Span,
     arg_count: usize,
-    coroutine_kind: Option<CoroutineKind>,
+    coroutine: Option<Box<CoroutineInfo<'tcx>>>,
 
     /// The current set of scopes, updated as we traverse;
     /// see the `scope` module for more details.
@@ -458,7 +458,6 @@ fn construct_fn<'tcx>(
 ) -> Body<'tcx> {
     let span = tcx.def_span(fn_def);
     let fn_id = tcx.local_def_id_to_hir_id(fn_def);
-    let coroutine_kind = tcx.coroutine_kind(fn_def);
 
     // The representation of thir for `-Zunpretty=thir-tree` relies on
     // the entry expression being the last element of `thir.exprs`.
@@ -488,17 +487,15 @@ fn construct_fn<'tcx>(
 
     let arguments = &thir.params;
 
-    let (yield_ty, return_ty) = if coroutine_kind.is_some() {
-        let coroutine_ty = arguments[thir::UPVAR_ENV_PARAM].ty;
-        let coroutine_sig = match coroutine_ty.kind() {
-            ty::Coroutine(_, gen_args, ..) => gen_args.as_coroutine().sig(),
-            _ => {
-                span_bug!(span, "coroutine w/o coroutine type: {:?}", coroutine_ty)
-            }
-        };
-        (Some(coroutine_sig.yield_ty), coroutine_sig.return_ty)
-    } else {
-        (None, fn_sig.output())
+    let return_ty = fn_sig.output();
+    let coroutine = match tcx.type_of(fn_def).instantiate_identity().kind() {
+        ty::Coroutine(_, args) => Some(Box::new(CoroutineInfo::initial(
+            tcx.coroutine_kind(fn_def).unwrap(),
+            args.as_coroutine().yield_ty(),
+            args.as_coroutine().resume_ty(),
+        ))),
+        ty::Closure(..) | ty::FnDef(..) => None,
+        ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"),
     };
 
     if let Some(custom_mir_attr) =
@@ -529,7 +526,7 @@ fn construct_fn<'tcx>(
         safety,
         return_ty,
         return_ty_span,
-        coroutine_kind,
+        coroutine,
     );
 
     let call_site_scope =
@@ -562,9 +559,7 @@ fn construct_fn<'tcx>(
     } else {
         None
     };
-    if yield_ty.is_some() {
-        body.coroutine.as_mut().unwrap().yield_ty = yield_ty;
-    }
+
     body
 }
 
@@ -629,9 +624,8 @@ fn construct_const<'a, 'tcx>(
 fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -> Body<'_> {
     let span = tcx.def_span(def_id);
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
-    let coroutine_kind = tcx.coroutine_kind(def_id);
 
-    let (inputs, output, yield_ty) = match tcx.def_kind(def_id) {
+    let (inputs, output, coroutine) = match tcx.def_kind(def_id) {
         DefKind::Const
         | DefKind::AssocConst
         | DefKind::AnonConst
@@ -644,31 +638,48 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
             );
             (sig.inputs().to_vec(), sig.output(), None)
         }
-        DefKind::Closure if coroutine_kind.is_some() => {
-            let coroutine_ty = tcx.type_of(def_id).instantiate_identity();
-            let ty::Coroutine(_, args) = coroutine_ty.kind() else {
-                bug!("expected type of coroutine-like closure to be a coroutine")
-            };
-            let args = args.as_coroutine();
-            let yield_ty = args.yield_ty();
-            let return_ty = args.return_ty();
-            (vec![coroutine_ty, args.resume_ty()], return_ty, Some(yield_ty))
-        }
         DefKind::Closure => {
             let closure_ty = tcx.type_of(def_id).instantiate_identity();
-            let ty::Closure(_, args) = closure_ty.kind() else {
-                bug!("expected type of closure to be a closure")
-            };
-            let args = args.as_closure();
-            let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig());
-            let self_ty = match args.kind() {
-                ty::ClosureKind::Fn => Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty),
-                ty::ClosureKind::FnMut => Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty),
-                ty::ClosureKind::FnOnce => closure_ty,
-            };
-            ([self_ty].into_iter().chain(sig.inputs().to_vec()).collect(), sig.output(), None)
+            match closure_ty.kind() {
+                ty::Closure(_, args) => {
+                    let args = args.as_closure();
+                    let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), args.sig());
+                    let self_ty = match args.kind() {
+                        ty::ClosureKind::Fn => {
+                            Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, closure_ty)
+                        }
+                        ty::ClosureKind::FnMut => {
+                            Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, closure_ty)
+                        }
+                        ty::ClosureKind::FnOnce => closure_ty,
+                    };
+                    (
+                        [self_ty].into_iter().chain(sig.inputs().to_vec()).collect(),
+                        sig.output(),
+                        None,
+                    )
+                }
+                ty::Coroutine(_, args) => {
+                    let args = args.as_coroutine();
+                    let resume_ty = args.resume_ty();
+                    let yield_ty = args.yield_ty();
+                    let return_ty = args.return_ty();
+                    (
+                        vec![closure_ty, args.resume_ty()],
+                        return_ty,
+                        Some(Box::new(CoroutineInfo::initial(
+                            tcx.coroutine_kind(def_id).unwrap(),
+                            yield_ty,
+                            resume_ty,
+                        ))),
+                    )
+                }
+                _ => {
+                    span_bug!(span, "expected type of closure body to be a closure or coroutine");
+                }
+            }
         }
-        dk => bug!("{:?} is not a body: {:?}", def_id, dk),
+        dk => span_bug!(span, "{:?} is not a body: {:?}", def_id, dk),
     };
 
     let source_info = SourceInfo { span, scope: OUTERMOST_SOURCE_SCOPE };
@@ -692,7 +703,7 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
 
     cfg.terminate(START_BLOCK, source_info, TerminatorKind::Unreachable);
 
-    let mut body = Body::new(
+    Body::new(
         MirSource::item(def_id.to_def_id()),
         cfg.basic_blocks,
         source_scopes,
@@ -701,13 +712,9 @@ fn construct_error(tcx: TyCtxt<'_>, def_id: LocalDefId, guar: ErrorGuaranteed) -
         inputs.len(),
         vec![],
         span,
-        coroutine_kind,
+        coroutine,
         Some(guar),
-    );
-
-    body.coroutine.as_mut().map(|gen| gen.yield_ty = yield_ty);
-
-    body
+    )
 }
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -721,7 +728,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         safety: Safety,
         return_ty: Ty<'tcx>,
         return_span: Span,
-        coroutine_kind: Option<CoroutineKind>,
+        coroutine: Option<Box<CoroutineInfo<'tcx>>>,
     ) -> Builder<'a, 'tcx> {
         let tcx = infcx.tcx;
         let attrs = tcx.hir().attrs(hir_id);
@@ -752,7 +759,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             cfg: CFG { basic_blocks: IndexVec::new() },
             fn_span: span,
             arg_count,
-            coroutine_kind,
+            coroutine,
             scopes: scope::Scopes::new(),
             block_context: BlockContext::new(),
             source_scopes: IndexVec::new(),
@@ -796,7 +803,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             self.arg_count,
             self.var_debug_info,
             self.fn_span,
-            self.coroutine_kind,
+            self.coroutine,
             None,
         )
     }
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 1a700ac7342..48b237f3ae6 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -706,7 +706,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // If we are emitting a `drop` statement, we need to have the cached
         // diverge cleanup pads ready in case that drop panics.
         let needs_cleanup = self.scopes.scopes.last().is_some_and(|scope| scope.needs_cleanup());
-        let is_coroutine = self.coroutine_kind.is_some();
+        let is_coroutine = self.coroutine.is_some();
         let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX };
 
         let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes");
@@ -960,7 +960,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // path, we only need to invalidate the cache for drops that happen on
         // the unwind or coroutine drop paths. This means that for
         // non-coroutines we don't need to invalidate caches for `DropKind::Storage`.
-        let invalidate_caches = needs_drop || self.coroutine_kind.is_some();
+        let invalidate_caches = needs_drop || self.coroutine.is_some();
         for scope in self.scopes.scopes.iter_mut().rev() {
             if invalidate_caches {
                 scope.invalidate_cache();
@@ -1073,7 +1073,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             return cached_drop;
         }
 
-        let is_coroutine = self.coroutine_kind.is_some();
+        let is_coroutine = self.coroutine.is_some();
         for scope in &mut self.scopes.scopes[uncached_scope..=target] {
             for drop in &scope.drops {
                 if is_coroutine || drop.kind == DropKind::Value {
@@ -1318,7 +1318,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
         blocks[ROOT_NODE] = continue_block;
 
         drops.build_mir::<ExitScopes>(&mut self.cfg, &mut blocks);
-        let is_coroutine = self.coroutine_kind.is_some();
+        let is_coroutine = self.coroutine.is_some();
 
         // Link the exit drop tree to unwind drop tree.
         if drops.drops.iter().any(|(drop, _)| drop.kind == DropKind::Value) {
@@ -1355,7 +1355,7 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> {
 
     /// Build the unwind and coroutine drop trees.
     pub(crate) fn build_drop_trees(&mut self) {
-        if self.coroutine_kind.is_some() {
+        if self.coroutine.is_some() {
             self.build_coroutine_drop_trees();
         } else {
             Self::build_unwind_tree(
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 2e8b6c19ec7..b4a02fae454 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -14,7 +14,7 @@ use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
 use rustc_session::lint::Level;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::symbol::Symbol;
-use rustc_span::Span;
+use rustc_span::{sym, Span};
 
 use std::mem;
 use std::ops::Bound;
@@ -144,11 +144,17 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
             let hir_context = self.tcx.local_def_id_to_hir_id(def);
             let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
             let mut inner_visitor = UnsafetyVisitor {
+                tcx: self.tcx,
                 thir: inner_thir,
                 hir_context,
                 safety_context,
+                body_target_features: self.body_target_features,
+                assignment_info: self.assignment_info,
+                in_union_destructure: false,
+                param_env: self.param_env,
+                inside_adt: false,
                 warnings: self.warnings,
-                ..*self
+                suggest_unsafe_block: self.suggest_unsafe_block,
             };
             inner_visitor.visit_expr(&inner_thir[expr]);
             // Unsafe blocks can be used in the inner body, make sure to take it into account
@@ -886,14 +892,15 @@ impl UnsafeOpKind {
     }
 }
 
-pub fn thir_check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
-    // THIR unsafeck is gated under `-Z thir-unsafeck`
+pub fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
+    // THIR unsafeck can be disabled with `-Z thir-unsafeck=off`
     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
         return;
     }
 
     // Closures and inline consts are handled by their owner, if it has a body
-    if tcx.is_typeck_child(def.to_def_id()) {
+    // Also, don't safety check custom MIR
+    if tcx.is_typeck_child(def.to_def_id()) || tcx.has_attr(def, sym::custom_mir) {
         return;
     }
 
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 8677cba6a7c..61ad99acf38 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -467,11 +467,11 @@ impl<'a> IntoDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
             level,
             fluent::mir_build_non_exhaustive_patterns_type_not_empty,
         );
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag.code(error_code!(E0004));
         let peeled_ty = self.ty.peel_refs();
-        diag.set_arg("ty", self.ty);
-        diag.set_arg("peeled_ty", peeled_ty);
+        diag.arg("ty", self.ty);
+        diag.arg("peeled_ty", peeled_ty);
 
         if let ty::Adt(def, _) = peeled_ty.kind() {
             let def_span = self
@@ -855,7 +855,7 @@ impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> {
     where
         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
     {
-        diag.set_arg("ty", self.ty);
+        diag.arg("ty", self.ty);
         let mut spans = MultiSpan::from(self.adt_def_span);
 
         for Variant { span } in self.variants {
diff --git a/compiler/rustc_mir_build/src/lib.rs b/compiler/rustc_mir_build/src/lib.rs
index a776e917de5..430c4ee3da7 100644
--- a/compiler/rustc_mir_build/src/lib.rs
+++ b/compiler/rustc_mir_build/src/lib.rs
@@ -31,7 +31,7 @@ pub fn provide(providers: &mut Providers) {
     providers.mir_built = build::mir_built;
     providers.closure_saved_names_of_captured_variables =
         build::closure_saved_names_of_captured_variables;
-    providers.thir_check_unsafety = check_unsafety::thir_check_unsafety;
+    providers.check_unsafety = check_unsafety::check_unsafety;
     providers.thir_body = thir::cx::thir_body;
     providers.thir_tree = thir::print::thir_tree;
     providers.thir_flat = thir::print::thir_flat;
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index 3a56b03948e..8d5e6cd4f41 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -1,7 +1,6 @@
 use rustc_ast as ast;
 use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
 use rustc_middle::ty::{self, ParamEnv, ScalarInt, TyCtxt};
-use rustc_span::DUMMY_SP;
 
 use crate::build::parse_float_into_scalar;
 
@@ -13,15 +12,15 @@ pub(crate) fn lit_to_const<'tcx>(
 
     let trunc = |n| {
         let param_ty = ParamEnv::reveal_all().and(ty);
-        let width = tcx
-            .layout_of(param_ty)
-            .map_err(|_| {
-                LitToConstError::Reported(tcx.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("couldn't compute width of literal: {:?}", lit_input.lit),
-                ))
-            })?
-            .size;
+        let width =
+            tcx.layout_of(param_ty)
+                .map_err(|_| {
+                    LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
+                        "couldn't compute width of literal: {:?}",
+                        lit_input.lit
+                    )))
+                })?
+                .size;
         trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
         let result = width.truncate(n);
         trace!("trunc result: {}", result);
@@ -60,20 +59,21 @@ pub(crate) fn lit_to_const<'tcx>(
         }
         (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
         (ast::LitKind::Float(n, _), ty::Float(fty)) => {
-            let bits = parse_float_into_scalar(*n, *fty, neg)
-                .ok_or_else(|| {
-                    LitToConstError::Reported(tcx.dcx().span_delayed_bug(
-                        DUMMY_SP,
-                        format!("couldn't parse float literal: {:?}", lit_input.lit),
-                    ))
-                })?
-                .assert_int();
+            let bits =
+                parse_float_into_scalar(*n, *fty, neg)
+                    .ok_or_else(|| {
+                        LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
+                            "couldn't parse float literal: {:?}",
+                            lit_input.lit
+                        )))
+                    })?
+                    .assert_int();
             ty::ValTree::from_scalar_int(bits)
         }
         (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
         (ast::LitKind::Err, _) => {
             return Err(LitToConstError::Reported(
-                tcx.dcx().span_delayed_bug(DUMMY_SP, "encountered LitKind::Err during mir build"),
+                tcx.dcx().delayed_bug("encountered LitKind::Err during mir build"),
             ));
         }
         _ => return Err(LitToConstError::TypeError),
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 8ec70c58c46..78d72b30284 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -855,13 +855,8 @@ impl<'tcx> Cx<'tcx> {
 
     fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
         let arm = Arm {
-            pattern: self.pattern_from_hir(arm.pat),
-            guard: arm.guard.as_ref().map(|g| match g {
-                hir::Guard::If(e) => Guard::If(self.mirror_expr(e)),
-                hir::Guard::IfLet(l) => {
-                    Guard::IfLet(self.pattern_from_hir(l.pat), self.mirror_expr(l.init))
-                }
-            }),
+            pattern: self.pattern_from_hir(&arm.pat),
+            guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
             body: self.mirror_expr(arm.body),
             lint_level: LintLevel::Explicit(arm.hir_id),
             scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
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 0bcc2a315ff..f6c5e4a5cd6 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -11,7 +11,9 @@ use rustc_arena::{DroplessArena, TypedArena};
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{
+    struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::LocalDefId;
@@ -55,7 +57,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
 }
 
 fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
-    struct_span_err!(sess.dcx(), sp, E0004, "{}", &error_message)
+    struct_span_code_err!(sess.dcx(), sp, E0004, "{}", &error_message)
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -100,20 +102,10 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
     #[instrument(level = "trace", skip(self))]
     fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
         self.with_lint_level(arm.lint_level, |this| {
-            match arm.guard {
-                Some(Guard::If(expr)) => {
-                    this.with_let_source(LetSource::IfLetGuard, |this| {
-                        this.visit_expr(&this.thir[expr])
-                    });
-                }
-                Some(Guard::IfLet(ref pat, expr)) => {
-                    this.with_let_source(LetSource::IfLetGuard, |this| {
-                        this.check_let(pat, Some(expr), pat.span);
-                        this.visit_pat(pat);
-                        this.visit_expr(&this.thir[expr]);
-                    });
-                }
-                None => {}
+            if let Some(expr) = arm.guard {
+                this.with_let_source(LetSource::IfLetGuard, |this| {
+                    this.visit_expr(&this.thir[expr])
+                });
             }
             this.visit_pat(&arm.pattern);
             this.visit_expr(&self.thir[arm.body]);
@@ -440,7 +432,13 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         }
 
         let scrut_ty = scrut.ty;
-        let report = analyze_match(&cx, &tarms, scrut_ty);
+        let report = match analyze_match(&cx, &tarms, scrut_ty) {
+            Ok(report) => report,
+            Err(err) => {
+                self.error = Err(err);
+                return;
+            }
+        };
 
         match source {
             // Don't report arm reachability of desugared `match $iter.into_iter() { iter => .. }`
@@ -554,7 +552,7 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
         let cx = self.new_cx(refutability, None, scrut, pat.span);
         let pat = self.lower_pattern(&cx, pat)?;
         let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
-        let report = analyze_match(&cx, &arms, pat.ty());
+        let report = analyze_match(&cx, &arms, pat.ty().inner())?;
         Ok((cx, report))
     }
 
@@ -972,7 +970,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
                 }
             } else if ty == cx.tcx.types.str_ {
                 err.note("`&str` cannot be matched exhaustively, so a wildcard `_` is necessary");
-            } else if cx.is_foreign_non_exhaustive_enum(ty) {
+            } else if cx.is_foreign_non_exhaustive_enum(cx.reveal_opaque_ty(ty)) {
                 err.note(format!("`{ty}` is marked as non-exhaustive, so a wildcard `_` is necessary to match exhaustively"));
             }
         }
@@ -1112,12 +1110,12 @@ fn collect_non_exhaustive_tys<'tcx>(
     non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
 ) {
     if matches!(pat.ctor(), Constructor::NonExhaustive) {
-        non_exhaustive_tys.insert(pat.ty());
+        non_exhaustive_tys.insert(pat.ty().inner());
     }
     if let Constructor::IntRange(range) = pat.ctor() {
         if cx.is_range_beyond_boundaries(range, pat.ty()) {
             // The range denotes the values before `isize::MIN` or the values after `usize::MAX`/`isize::MAX`.
-            non_exhaustive_tys.insert(pat.ty());
+            non_exhaustive_tys.insert(pat.ty().inner());
         }
     }
     pat.iter_fields()
diff --git a/compiler/rustc_mir_build/src/thir/print.rs b/compiler/rustc_mir_build/src/thir/print.rs
index 28be3139905..267ea3aa3e1 100644
--- a/compiler/rustc_mir_build/src/thir/print.rs
+++ b/compiler/rustc_mir_build/src/thir/print.rs
@@ -593,9 +593,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
         print_indented!(self, "pattern: ", depth_lvl + 1);
         self.print_pat(pattern, depth_lvl + 2);
 
-        if let Some(guard) = guard {
+        if let Some(guard) = *guard {
             print_indented!(self, "guard: ", depth_lvl + 1);
-            self.print_guard(guard, depth_lvl + 2);
+            self.print_expr(guard, depth_lvl + 2);
         } else {
             print_indented!(self, "guard: None", depth_lvl + 1);
         }
@@ -764,27 +764,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
         print_indented!(self, "}", depth_lvl);
     }
 
-    fn print_guard(&mut self, guard: &Guard<'tcx>, depth_lvl: usize) {
-        print_indented!(self, "Guard {", depth_lvl);
-
-        match guard {
-            Guard::If(expr_id) => {
-                print_indented!(self, "If (", depth_lvl + 1);
-                self.print_expr(*expr_id, depth_lvl + 2);
-                print_indented!(self, ")", depth_lvl + 1);
-            }
-            Guard::IfLet(pat, expr_id) => {
-                print_indented!(self, "IfLet (", depth_lvl + 1);
-                self.print_pat(pat, depth_lvl + 2);
-                print_indented!(self, ",", depth_lvl + 1);
-                self.print_expr(*expr_id, depth_lvl + 2);
-                print_indented!(self, ")", depth_lvl + 1);
-            }
-        }
-
-        print_indented!(self, "}", depth_lvl);
-    }
-
     fn print_closure_expr(&mut self, expr: &ClosureExpr<'tcx>, depth_lvl: usize) {
         let ClosureExpr { closure_id, args, upvars, movability, fake_reads } = expr;
 
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index d94d96c1115..582c2c0c6b6 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -131,7 +131,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                 &AggregateKind::Closure(def_id, _) | &AggregateKind::Coroutine(def_id, _) => {
                     let def_id = def_id.expect_local();
                     let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
-                        self.tcx.unsafety_check_result(def_id);
+                        self.tcx.mir_unsafety_check_result(def_id);
                     self.register_violations(violations, used_unsafe_blocks.items().copied());
                 }
             },
@@ -153,7 +153,7 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     if self.tcx.def_kind(def_id) == DefKind::InlineConst {
                         let local_def_id = def_id.expect_local();
                         let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
-                            self.tcx.unsafety_check_result(local_def_id);
+                            self.tcx.mir_unsafety_check_result(local_def_id);
                         self.register_violations(violations, used_unsafe_blocks.items().copied());
                     }
                 }
@@ -390,7 +390,7 @@ impl<'tcx> UnsafetyChecker<'_, 'tcx> {
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
-    *providers = Providers { unsafety_check_result, ..*providers };
+    *providers = Providers { mir_unsafety_check_result, ..*providers };
 }
 
 /// Context information for [`UnusedUnsafeVisitor`] traversal,
@@ -490,7 +490,7 @@ fn check_unused_unsafe(
     unused_unsafes
 }
 
-fn unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult {
+fn mir_unsafety_check_result(tcx: TyCtxt<'_>, def: LocalDefId) -> &UnsafetyCheckResult {
     debug!("unsafety_violations({:?})", def);
 
     // N.B., this borrow is valid because all the consumers of
@@ -538,7 +538,8 @@ pub fn check_unsafety(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         return;
     }
 
-    let UnsafetyCheckResult { violations, unused_unsafes, .. } = tcx.unsafety_check_result(def_id);
+    let UnsafetyCheckResult { violations, unused_unsafes, .. } =
+        tcx.mir_unsafety_check_result(def_id);
     // Only suggest wrapping the entire function body in an unsafe block once
     let mut suggest_unsafe_block = true;
 
diff --git a/compiler/rustc_mir_transform/src/const_goto.rs b/compiler/rustc_mir_transform/src/const_goto.rs
index 3884346076e..cb5b66b314d 100644
--- a/compiler/rustc_mir_transform/src/const_goto.rs
+++ b/compiler/rustc_mir_transform/src/const_goto.rs
@@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
             simplify_locals(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 99eecb567f2..d0bbca08a40 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs
@@ -670,6 +670,8 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
         // This loop can get very hot for some bodies: it check each local in each bb.
         // To avoid this quadratic behaviour, we only clear the locals that were modified inside
         // the current block.
+        // The order in which we remove consts does not matter.
+        #[allow(rustc::potential_query_instability)]
         for local in written_only_inside_own_block_locals.drain() {
             debug_assert_eq!(
                 self.ecx.machine.can_const_prop[local],
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index ce1a36cf670..379e96e263c 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -942,6 +942,7 @@ fn compute_storage_conflicts<'mir, 'tcx>(
         body,
         saved_locals: saved_locals,
         local_conflicts: BitMatrix::from_row_n(&ineligible_locals, body.local_decls.len()),
+        eligible_storage_live: BitSet::new_empty(body.local_decls.len()),
     };
 
     requires_storage.visit_reachable_with(body, &mut visitor);
@@ -978,6 +979,8 @@ struct StorageConflictVisitor<'mir, 'tcx, 's> {
     // FIXME(tmandry): Consider using sparse bitsets here once we have good
     // benchmarks for coroutines.
     local_conflicts: BitMatrix<Local, Local>,
+    // We keep this bitset as a buffer to avoid reallocating memory.
+    eligible_storage_live: BitSet<Local>,
 }
 
 impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
@@ -1009,19 +1012,19 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
 impl StorageConflictVisitor<'_, '_, '_> {
     fn apply_state(&mut self, flow_state: &BitSet<Local>, loc: Location) {
         // Ignore unreachable blocks.
-        if self.body.basic_blocks[loc.block].terminator().kind == TerminatorKind::Unreachable {
+        if let TerminatorKind::Unreachable = self.body.basic_blocks[loc.block].terminator().kind {
             return;
         }
 
-        let mut eligible_storage_live = flow_state.clone();
-        eligible_storage_live.intersect(&**self.saved_locals);
+        self.eligible_storage_live.clone_from(flow_state);
+        self.eligible_storage_live.intersect(&**self.saved_locals);
 
-        for local in eligible_storage_live.iter() {
-            self.local_conflicts.union_row_with(&eligible_storage_live, local);
+        for local in self.eligible_storage_live.iter() {
+            self.local_conflicts.union_row_with(&self.eligible_storage_live, local);
         }
 
-        if eligible_storage_live.count() > 1 {
-            trace!("at {:?}, eligible_storage_live={:?}", loc, eligible_storage_live);
+        if self.eligible_storage_live.count() > 1 {
+            trace!("at {:?}, eligible_storage_live={:?}", loc, self.eligible_storage_live);
         }
     }
 }
@@ -1733,6 +1736,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
         }
 
         body.coroutine.as_mut().unwrap().yield_ty = None;
+        body.coroutine.as_mut().unwrap().resume_ty = None;
         body.coroutine.as_mut().unwrap().coroutine_layout = Some(layout);
 
         // Insert `drop(coroutine_struct)` which is used to drop upvars for coroutines in
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index aa7b6b02f74..dcd7014f4fc 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -23,7 +23,7 @@ use rustc_middle::mir::{
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::SourceMap;
-use rustc_span::{Span, Symbol};
+use rustc_span::{BytePos, Pos, RelativeBytePos, Span, Symbol};
 
 /// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
 /// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
@@ -107,6 +107,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
         );
 
         let mappings = self.create_mappings(&coverage_spans, &coverage_counters);
+        if mappings.is_empty() {
+            // No spans could be converted into valid mappings, so skip this function.
+            debug!("no spans could be converted into valid mappings; skipping");
+            return;
+        }
+
         self.inject_coverage_statements(bcb_has_coverage_spans, &coverage_counters);
 
         self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
@@ -148,9 +154,9 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
             // Flatten the spans into individual term/span pairs.
             .flat_map(|(term, spans)| spans.iter().map(move |&span| (term, span)))
             // Convert each span to a code region, and create the final mapping.
-            .map(|(term, span)| {
-                let code_region = make_code_region(source_map, file_name, span, body_span);
-                Mapping { term, code_region }
+            .filter_map(|(term, span)| {
+                let code_region = make_code_region(source_map, file_name, span, body_span)?;
+                Some(Mapping { term, code_region })
             })
             .collect::<Vec<_>>()
     }
@@ -252,13 +258,22 @@ fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb
     data.statements.insert(0, statement);
 }
 
-/// Convert the Span into its file name, start line and column, and end line and column
+/// Convert the Span into its file name, start line and column, and end line and column.
+///
+/// Line numbers and column numbers are 1-based. Unlike most column numbers emitted by
+/// the compiler, these column numbers are denoted in **bytes**, because that's what
+/// LLVM's `llvm-cov` tool expects to see in coverage maps.
+///
+/// Returns `None` if the conversion failed for some reason. This shouldn't happen,
+/// but it's hard to rule out entirely (especially in the presence of complex macros
+/// or other expansions), and if it does happen then skipping a span or function is
+/// better than an ICE or `llvm-cov` failure that the user might have no way to avoid.
 fn make_code_region(
     source_map: &SourceMap,
     file_name: Symbol,
     span: Span,
     body_span: Span,
-) -> CodeRegion {
+) -> Option<CodeRegion> {
     debug!(
         "Called make_code_region(file_name={}, span={}, body_span={})",
         file_name,
@@ -266,27 +281,62 @@ fn make_code_region(
         source_map.span_to_diagnostic_string(body_span)
     );
 
-    let (file, mut start_line, mut start_col, mut end_line, mut end_col) =
-        source_map.span_to_location_info(span);
-    if span.hi() == span.lo() {
-        // Extend an empty span by one character so the region will be counted.
-        if span.hi() == body_span.hi() {
-            start_col = start_col.saturating_sub(1);
-        } else {
-            end_col = start_col + 1;
-        }
+    let lo = span.lo();
+    let hi = span.hi();
+
+    let file = source_map.lookup_source_file(lo);
+    if !file.contains(hi) {
+        debug!(?span, ?file, ?lo, ?hi, "span crosses multiple files; skipping");
+        return None;
+    }
+
+    // Column numbers need to be in bytes, so we can't use the more convenient
+    // `SourceMap` methods for looking up file coordinates.
+    let rpos_and_line_and_byte_column = |pos: BytePos| -> Option<(RelativeBytePos, usize, usize)> {
+        let rpos = file.relative_position(pos);
+        let line_index = file.lookup_line(rpos)?;
+        let line_start = file.lines()[line_index];
+        // Line numbers and column numbers are 1-based, so add 1 to each.
+        Some((rpos, line_index + 1, (rpos - line_start).to_usize() + 1))
     };
-    if let Some(file) = file {
-        start_line = source_map.doctest_offset_line(&file.name, start_line);
-        end_line = source_map.doctest_offset_line(&file.name, end_line);
+
+    let (lo_rpos, mut start_line, mut start_col) = rpos_and_line_and_byte_column(lo)?;
+    let (hi_rpos, mut end_line, mut end_col) = rpos_and_line_and_byte_column(hi)?;
+
+    // If the span is empty, try to expand it horizontally by one character's
+    // worth of bytes, so that it is more visible in `llvm-cov` reports.
+    // We do this after resolving line/column numbers, so that empty spans at the
+    // end of a line get an extra column instead of wrapping to the next line.
+    if span.is_empty()
+        && body_span.contains(span)
+        && let Some(src) = &file.src
+    {
+        // Prefer to expand the end position, if it won't go outside the body span.
+        if hi < body_span.hi() {
+            let hi_rpos = hi_rpos.to_usize();
+            let nudge_bytes = src.ceil_char_boundary(hi_rpos + 1) - hi_rpos;
+            end_col += nudge_bytes;
+        } else if lo > body_span.lo() {
+            let lo_rpos = lo_rpos.to_usize();
+            let nudge_bytes = lo_rpos - src.floor_char_boundary(lo_rpos - 1);
+            // Subtract the nudge, but don't go below column 1.
+            start_col = start_col.saturating_sub(nudge_bytes).max(1);
+        }
+        // If neither nudge could be applied, stick with the empty span coordinates.
     }
-    CodeRegion {
+
+    // Apply an offset so that code in doctests has correct line numbers.
+    // FIXME(#79417): Currently we have no way to offset doctest _columns_.
+    start_line = source_map.doctest_offset_line(&file.name, start_line);
+    end_line = source_map.doctest_offset_line(&file.name, end_line);
+
+    Some(CodeRegion {
         file_name,
         start_line: start_line as u32,
         start_col: start_col as u32,
         end_line: end_line as u32,
         end_col: end_col as u32,
-    }
+    })
 }
 
 fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index ed091752187..5983189984d 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,11 +1,9 @@
-use std::cell::OnceCell;
-
 use rustc_data_structures::graph::WithNumNodes;
 use rustc_index::IndexVec;
 use rustc_middle::mir;
-use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP};
+use rustc_span::{BytePos, Span, DUMMY_SP};
 
-use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
+use super::graph::{BasicCoverageBlock, CoverageGraph};
 use crate::coverage::ExtractedHirInfo;
 
 mod from_mir;
@@ -70,35 +68,17 @@ impl CoverageSpans {
 /// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
 #[derive(Debug, Clone)]
 struct CoverageSpan {
-    pub span: Span,
-    pub expn_span: Span,
-    pub current_macro_or_none: OnceCell<Option<Symbol>>,
-    pub bcb: BasicCoverageBlock,
+    span: Span,
+    bcb: BasicCoverageBlock,
     /// List of all the original spans from MIR that have been merged into this
     /// span. Mainly used to precisely skip over gaps when truncating a span.
-    pub merged_spans: Vec<Span>,
-    pub is_closure: bool,
+    merged_spans: Vec<Span>,
+    is_closure: bool,
 }
 
 impl CoverageSpan {
-    pub fn for_fn_sig(fn_sig_span: Span) -> Self {
-        Self::new(fn_sig_span, fn_sig_span, START_BCB, false)
-    }
-
-    pub(super) fn new(
-        span: Span,
-        expn_span: Span,
-        bcb: BasicCoverageBlock,
-        is_closure: bool,
-    ) -> Self {
-        Self {
-            span,
-            expn_span,
-            current_macro_or_none: Default::default(),
-            bcb,
-            merged_spans: vec![span],
-            is_closure,
-        }
+    fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
+        Self { span, bcb, merged_spans: vec![span], is_closure }
     }
 
     pub fn merge_from(&mut self, other: &Self) {
@@ -123,37 +103,6 @@ impl CoverageSpan {
     pub fn is_in_same_bcb(&self, other: &Self) -> bool {
         self.bcb == other.bcb
     }
-
-    /// If the span is part of a macro, returns the macro name symbol.
-    pub fn current_macro(&self) -> Option<Symbol> {
-        self.current_macro_or_none
-            .get_or_init(|| {
-                if let ExpnKind::Macro(MacroKind::Bang, current_macro) =
-                    self.expn_span.ctxt().outer_expn_data().kind
-                {
-                    return Some(current_macro);
-                }
-                None
-            })
-            .map(|symbol| symbol)
-    }
-
-    /// If the span is part of a macro, and the macro is visible (expands directly to the given
-    /// body_span), returns the macro name symbol.
-    pub fn visible_macro(&self, body_span: Span) -> Option<Symbol> {
-        let current_macro = self.current_macro()?;
-        let parent_callsite = self.expn_span.parent_callsite()?;
-
-        // In addition to matching the context of the body span, the parent callsite
-        // must also be the source callsite, i.e. the parent must have no parent.
-        let is_visible_macro =
-            parent_callsite.parent_callsite().is_none() && parent_callsite.eq_ctxt(body_span);
-        is_visible_macro.then_some(current_macro)
-    }
-
-    pub fn is_macro_expansion(&self) -> bool {
-        self.current_macro().is_some()
-    }
 }
 
 /// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
@@ -164,10 +113,6 @@ impl CoverageSpan {
 ///    execution
 ///  * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
 struct CoverageSpansGenerator<'a> {
-    /// A `Span` covering the function body of the MIR (typically from left curly brace to right
-    /// curly brace).
-    body_span: Span,
-
     /// The BasicCoverageBlock Control Flow Graph (BCB CFG).
     basic_coverage_blocks: &'a CoverageGraph,
 
@@ -244,7 +189,6 @@ impl<'a> CoverageSpansGenerator<'a> {
         );
 
         let coverage_spans = Self {
-            body_span: hir_info.body_span,
             basic_coverage_blocks,
             sorted_spans_iter: sorted_spans.into_iter(),
             some_curr: None,
@@ -266,7 +210,6 @@ impl<'a> CoverageSpansGenerator<'a> {
             // span-processing steps don't make sense yet.
             if self.some_prev.is_none() {
                 debug!("  initial span");
-                self.maybe_push_macro_name_span();
                 continue;
             }
 
@@ -278,7 +221,6 @@ impl<'a> CoverageSpansGenerator<'a> {
                 debug!("  same bcb (and neither is a closure), merge with prev={prev:?}");
                 let prev = self.take_prev();
                 self.curr_mut().merge_from(&prev);
-                self.maybe_push_macro_name_span();
             // Note that curr.span may now differ from curr_original_span
             } else if prev.span.hi() <= curr.span.lo() {
                 debug!(
@@ -286,7 +228,6 @@ impl<'a> CoverageSpansGenerator<'a> {
                 );
                 let prev = self.take_prev();
                 self.refined_spans.push(prev);
-                self.maybe_push_macro_name_span();
             } else if prev.is_closure {
                 // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
                 // next iter
@@ -297,35 +238,11 @@ impl<'a> CoverageSpansGenerator<'a> {
             } else if curr.is_closure {
                 self.carve_out_span_for_closure();
             } else if self.prev_original_span == curr.span {
-                // Note that this compares the new (`curr`) span to `prev_original_span`.
-                // In this branch, the actual span byte range of `prev_original_span` is not
-                // important. What is important is knowing whether the new `curr` span was
-                // **originally** the same as the original span of `prev()`. The original spans
-                // reflect their original sort order, and for equal spans, conveys a partial
-                // ordering based on CFG dominator priority.
-                if prev.is_macro_expansion() && curr.is_macro_expansion() {
-                    // Macros that expand to include branching (such as
-                    // `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or
-                    // `trace!()`) typically generate callee spans with identical
-                    // ranges (typically the full span of the macro) for all
-                    // `BasicBlocks`. This makes it impossible to distinguish
-                    // the condition (`if val1 != val2`) from the optional
-                    // branched statements (such as the call to `panic!()` on
-                    // assert failure). In this case it is better (or less
-                    // worse) to drop the optional branch bcbs and keep the
-                    // non-conditional statements, to count when reached.
-                    debug!(
-                        "  curr and prev are part of a macro expansion, and curr has the same span \
-                        as prev, but is in a different bcb. Drop curr and keep prev for next iter. \
-                        prev={prev:?}",
-                    );
-                    self.take_curr(); // Discards curr.
-                } else {
-                    self.update_pending_dups();
-                }
+                // `prev` and `curr` have the same span, or would have had the
+                // same span before `prev` was modified by other spans.
+                self.update_pending_dups();
             } else {
                 self.cutoff_prev_at_overlapping_curr();
-                self.maybe_push_macro_name_span();
             }
         }
 
@@ -360,41 +277,6 @@ impl<'a> CoverageSpansGenerator<'a> {
         self.refined_spans
     }
 
-    /// If `curr` is part of a new macro expansion, carve out and push a separate
-    /// span that ends just after the macro name and its subsequent `!`.
-    fn maybe_push_macro_name_span(&mut self) {
-        let curr = self.curr();
-
-        let Some(visible_macro) = curr.visible_macro(self.body_span) else { return };
-        if let Some(prev) = &self.some_prev
-            && prev.expn_span.eq_ctxt(curr.expn_span)
-        {
-            return;
-        }
-
-        // The split point is relative to `curr_original_span`,
-        // because `curr.span` may have been merged with preceding spans.
-        let split_point_after_macro_bang = self.curr_original_span.lo()
-            + BytePos(visible_macro.as_str().len() as u32)
-            + BytePos(1); // add 1 for the `!`
-        debug_assert!(split_point_after_macro_bang <= curr.span.hi());
-        if split_point_after_macro_bang > curr.span.hi() {
-            // Something is wrong with the macro name span;
-            // return now to avoid emitting malformed mappings (e.g. #117788).
-            return;
-        }
-
-        let mut macro_name_cov = curr.clone();
-        macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang);
-        self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang);
-
-        debug!(
-            "  and curr starts a new macro expansion, so add a new span just for \
-            the macro `{visible_macro}!`, new span={macro_name_cov:?}",
-        );
-        self.refined_spans.push(macro_name_cov);
-    }
-
     #[track_caller]
     fn curr(&self) -> &CoverageSpan {
         self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 8f6592afe85..1b6dfccd574 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -1,11 +1,14 @@
 use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir::{
     self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
     TerminatorKind,
 };
-use rustc_span::Span;
+use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
 
-use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
+use crate::coverage::graph::{
+    BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
+};
 use crate::coverage::spans::CoverageSpan;
 use crate::coverage::ExtractedHirInfo;
 
@@ -15,26 +18,29 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
     basic_coverage_blocks: &CoverageGraph,
 ) -> Vec<CoverageSpan> {
     let &ExtractedHirInfo { is_async_fn, fn_sig_span, body_span, .. } = hir_info;
+
+    let mut initial_spans = vec![SpanFromMir::for_fn_sig(fn_sig_span)];
+
     if is_async_fn {
         // An async function desugars into a function that returns a future,
         // with the user code wrapped in a closure. Any spans in the desugared
-        // outer function will be unhelpful, so just produce a single span
-        // associating the function signature with its entry BCB.
-        return vec![CoverageSpan::for_fn_sig(fn_sig_span)];
-    }
-
-    let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2);
-    for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
-        initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
-    }
+        // outer function will be unhelpful, so just keep the signature span
+        // and ignore all of the spans in the MIR body.
+    } else {
+        for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
+            initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
+        }
 
-    if initial_spans.is_empty() {
-        // This can happen if, for example, the function is unreachable (contains only a
-        // `BasicBlock`(s) with an `Unreachable` terminator).
-        return initial_spans;
+        // If no spans were extracted from the body, discard the signature span.
+        // FIXME: This preserves existing behavior; consider getting rid of it.
+        if initial_spans.len() == 1 {
+            initial_spans.clear();
+        }
     }
 
-    initial_spans.push(CoverageSpan::for_fn_sig(fn_sig_span));
+    initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
+    remove_unwanted_macro_spans(&mut initial_spans);
+    split_visible_macro_spans(&mut initial_spans);
 
     initial_spans.sort_by(|a, b| {
         // First sort by span start.
@@ -53,7 +59,62 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
             .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
     });
 
-    initial_spans
+    initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>()
+}
+
+/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
+/// multiple condition/consequent blocks that have the span of the whole macro
+/// invocation, which is unhelpful. Keeping only the first such span seems to
+/// give better mappings, so remove the others.
+///
+/// (The input spans should be sorted in BCB dominator order, so that the
+/// retained "first" span is likely to dominate the others.)
+fn remove_unwanted_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
+    let mut seen_macro_spans = FxHashSet::default();
+    initial_spans.retain(|covspan| {
+        // Ignore (retain) closure spans and non-macro-expansion spans.
+        if covspan.is_closure || covspan.visible_macro.is_none() {
+            return true;
+        }
+
+        // Retain only the first macro-expanded covspan with this span.
+        seen_macro_spans.insert(covspan.span)
+    });
+}
+
+/// When a span corresponds to a macro invocation that is visible from the
+/// function body, split it into two parts. The first part covers just the
+/// macro name plus `!`, and the second part covers the rest of the macro
+/// invocation. This seems to give better results for code that uses macros.
+fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
+    let mut extra_spans = vec![];
+
+    initial_spans.retain(|covspan| {
+        if covspan.is_closure {
+            return true;
+        }
+
+        let Some(visible_macro) = covspan.visible_macro else { return true };
+
+        let split_len = visible_macro.as_str().len() as u32 + 1;
+        let (before, after) = covspan.span.split_at(split_len);
+        if !covspan.span.contains(before) || !covspan.span.contains(after) {
+            // Something is unexpectedly wrong with the split point.
+            // The debug assertion in `split_at` will have already caught this,
+            // but in release builds it's safer to do nothing and maybe get a
+            // bug report for unexpected coverage, rather than risk an ICE.
+            return true;
+        }
+
+        assert!(!covspan.is_closure);
+        extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb, false));
+        extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb, false));
+        false // Discard the original covspan that we just split.
+    });
+
+    // The newly-split spans are added at the end, so any previous sorting
+    // is not preserved.
+    initial_spans.extend(extra_spans);
 }
 
 // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
@@ -66,22 +127,24 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
     body_span: Span,
     bcb: BasicCoverageBlock,
     bcb_data: &'a BasicCoverageBlockData,
-) -> impl Iterator<Item = CoverageSpan> + Captures<'a> + Captures<'tcx> {
+) -> impl Iterator<Item = SpanFromMir> + Captures<'a> + Captures<'tcx> {
     bcb_data.basic_blocks.iter().flat_map(move |&bb| {
         let data = &mir_body[bb];
 
         let statement_spans = data.statements.iter().filter_map(move |statement| {
             let expn_span = filtered_statement_span(statement)?;
-            let span = unexpand_into_body_span(expn_span, body_span)?;
+            let (span, visible_macro) =
+                unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
 
-            Some(CoverageSpan::new(span, expn_span, bcb, is_closure_or_coroutine(statement)))
+            Some(SpanFromMir::new(span, visible_macro, bcb, is_closure_or_coroutine(statement)))
         });
 
         let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| {
             let expn_span = filtered_terminator_span(terminator)?;
-            let span = unexpand_into_body_span(expn_span, body_span)?;
+            let (span, visible_macro) =
+                unexpand_into_body_span_with_visible_macro(expn_span, body_span)?;
 
-            Some(CoverageSpan::new(span, expn_span, bcb, false))
+            Some(SpanFromMir::new(span, visible_macro, bcb, false))
         });
 
         statement_spans.chain(terminator_span)
@@ -202,7 +265,83 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
 ///
 /// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
 /// etc.).
-#[inline]
-fn unexpand_into_body_span(span: Span, body_span: Span) -> Option<Span> {
-    span.find_ancestor_inside_same_ctxt(body_span)
+fn unexpand_into_body_span_with_visible_macro(
+    original_span: Span,
+    body_span: Span,
+) -> Option<(Span, Option<Symbol>)> {
+    let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
+
+    let visible_macro = prev
+        .map(|prev| match prev.ctxt().outer_expn_data().kind {
+            ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
+            _ => None,
+        })
+        .flatten();
+
+    Some((span, visible_macro))
+}
+
+/// Walks through the expansion ancestors of `original_span` to find a span that
+/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`.
+/// The ancestor that was traversed just before the matching span (if any) is
+/// also returned.
+///
+/// For example, a return value of `Some((ancestor, Some(prev))` means that:
+/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
+/// - `ancestor == prev.parent_callsite()`
+///
+/// [`SyntaxContext`]: rustc_span::SyntaxContext
+fn unexpand_into_body_span_with_prev(
+    original_span: Span,
+    body_span: Span,
+) -> Option<(Span, Option<Span>)> {
+    let mut prev = None;
+    let mut curr = original_span;
+
+    while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
+        prev = Some(curr);
+        curr = curr.parent_callsite()?;
+    }
+
+    debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span));
+    if let Some(prev) = prev {
+        debug_assert_eq!(Some(curr), prev.parent_callsite());
+    }
+
+    Some((curr, prev))
+}
+
+#[derive(Debug)]
+struct SpanFromMir {
+    /// A span that has been extracted from MIR and then "un-expanded" back to
+    /// within the current function's `body_span`. After various intermediate
+    /// processing steps, this span is emitted as part of the final coverage
+    /// mappings.
+    ///
+    /// With the exception of `fn_sig_span`, this should always be contained
+    /// within `body_span`.
+    span: Span,
+    visible_macro: Option<Symbol>,
+    bcb: BasicCoverageBlock,
+    is_closure: bool,
+}
+
+impl SpanFromMir {
+    fn for_fn_sig(fn_sig_span: Span) -> Self {
+        Self::new(fn_sig_span, None, START_BCB, false)
+    }
+
+    fn new(
+        span: Span,
+        visible_macro: Option<Symbol>,
+        bcb: BasicCoverageBlock,
+        is_closure: bool,
+    ) -> Self {
+        Self { span, visible_macro, bcb, is_closure }
+    }
+
+    fn into_coverage_span(self) -> CoverageSpan {
+        let Self { span, visible_macro: _, bcb, is_closure } = self;
+        CoverageSpan::new(span, bcb, is_closure)
+    }
 }
diff --git a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
index b40b2ec8bfd..824974970bb 100644
--- a/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
+++ b/compiler/rustc_mir_transform/src/deduplicate_blocks.rs
@@ -25,7 +25,7 @@ impl<'tcx> MirPass<'tcx> for DeduplicateBlocks {
         if has_opts_to_apply {
             let mut opt_applier = OptApplier { tcx, duplicates };
             opt_applier.visit_body(body);
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 15502adfb5a..49b0edc0db8 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -131,11 +131,8 @@
 //! [attempt 2]: https://github.com/rust-lang/rust/pull/71003
 //! [attempt 3]: https://github.com/rust-lang/rust/pull/72632
 
-use std::collections::hash_map::{Entry, OccupiedEntry};
-
-use crate::simplify::remove_dead_blocks;
 use crate::MirPass;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxIndexMap, IndexEntry, IndexOccupiedEntry};
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
 use rustc_middle::mir::HasLocalDecls;
@@ -212,7 +209,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
             let mut merged_locals: BitSet<Local> = BitSet::new_empty(body.local_decls.len());
 
             // This is the set of merges we will apply this round. It is a subset of the candidates.
-            let mut merges = FxHashMap::default();
+            let mut merges = FxIndexMap::default();
 
             for (src, candidates) in candidates.c.iter() {
                 if merged_locals.contains(*src) {
@@ -241,12 +238,6 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
             apply_merges(body, tcx, &merges, &merged_locals);
         }
 
-        if round_count != 0 {
-            // Merging can introduce overlap between moved arguments and/or call destination in an
-            // unreachable code, which validator considers to be ill-formed.
-            remove_dead_blocks(body);
-        }
-
         trace!(round_count);
     }
 }
@@ -257,8 +248,8 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
 /// frequently. Everything with a `&'alloc` lifetime points into here.
 #[derive(Default)]
 struct Allocations {
-    candidates: FxHashMap<Local, Vec<Local>>,
-    candidates_reverse: FxHashMap<Local, Vec<Local>>,
+    candidates: FxIndexMap<Local, Vec<Local>>,
+    candidates_reverse: FxIndexMap<Local, Vec<Local>>,
     write_info: WriteInfo,
     // PERF: Do this for `MaybeLiveLocals` allocations too.
 }
@@ -279,11 +270,11 @@ struct Candidates<'alloc> {
     ///
     /// We will still report that we would like to merge `_1` and `_2` in an attempt to allow us to
     /// remove that assignment.
-    c: &'alloc mut FxHashMap<Local, Vec<Local>>,
+    c: &'alloc mut FxIndexMap<Local, Vec<Local>>,
     /// A reverse index of the `c` set; if the `c` set contains `a => Place { local: b, proj }`,
     /// then this contains `b => a`.
     // PERF: Possibly these should be `SmallVec`s?
-    reverse: &'alloc mut FxHashMap<Local, Vec<Local>>,
+    reverse: &'alloc mut FxIndexMap<Local, Vec<Local>>,
 }
 
 //////////////////////////////////////////////////////////
@@ -294,7 +285,7 @@ struct Candidates<'alloc> {
 fn apply_merges<'tcx>(
     body: &mut Body<'tcx>,
     tcx: TyCtxt<'tcx>,
-    merges: &FxHashMap<Local, Local>,
+    merges: &FxIndexMap<Local, Local>,
     merged_locals: &BitSet<Local>,
 ) {
     let mut merger = Merger { tcx, merges, merged_locals };
@@ -303,7 +294,7 @@ fn apply_merges<'tcx>(
 
 struct Merger<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    merges: &'a FxHashMap<Local, Local>,
+    merges: &'a FxIndexMap<Local, Local>,
     merged_locals: &'a BitSet<Local>,
 }
 
@@ -386,7 +377,7 @@ impl<'alloc> Candidates<'alloc> {
 
     /// `vec_filter_candidates` but for an `Entry`
     fn entry_filter_candidates(
-        mut entry: OccupiedEntry<'_, Local, Vec<Local>>,
+        mut entry: IndexOccupiedEntry<'_, Local, Vec<Local>>,
         p: Local,
         f: impl FnMut(Local) -> CandidateFilter,
         at: Location,
@@ -406,7 +397,7 @@ impl<'alloc> Candidates<'alloc> {
         at: Location,
     ) {
         // Cover the cases where `p` appears as a `src`
-        if let Entry::Occupied(entry) = self.c.entry(p) {
+        if let IndexEntry::Occupied(entry) = self.c.entry(p) {
             Self::entry_filter_candidates(entry, p, &mut f, at);
         }
         // And the cases where `p` appears as a `dest`
@@ -419,7 +410,7 @@ impl<'alloc> Candidates<'alloc> {
             if f(*src) == CandidateFilter::Keep {
                 return true;
             }
-            let Entry::Occupied(entry) = self.c.entry(*src) else {
+            let IndexEntry::Occupied(entry) = self.c.entry(*src) else {
                 return false;
             };
             Self::entry_filter_candidates(
@@ -728,8 +719,8 @@ fn places_to_candidate_pair<'tcx>(
 fn find_candidates<'alloc, 'tcx>(
     body: &Body<'tcx>,
     borrowed: &BitSet<Local>,
-    candidates: &'alloc mut FxHashMap<Local, Vec<Local>>,
-    candidates_reverse: &'alloc mut FxHashMap<Local, Vec<Local>>,
+    candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
+    candidates_reverse: &'alloc mut FxIndexMap<Local, Vec<Local>>,
 ) -> Candidates<'alloc> {
     candidates.clear();
     candidates_reverse.clear();
@@ -751,7 +742,7 @@ fn find_candidates<'alloc, 'tcx>(
 
 struct FindAssignments<'a, 'alloc, 'tcx> {
     body: &'a Body<'tcx>,
-    candidates: &'alloc mut FxHashMap<Local, Vec<Local>>,
+    candidates: &'alloc mut FxIndexMap<Local, Vec<Local>>,
     borrowed: &'a BitSet<Local>,
 }
 
diff --git a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
index 6eb6cb069fe..0d600f0f937 100644
--- a/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
+++ b/compiler/rustc_mir_transform/src/early_otherwise_branch.rs
@@ -212,7 +212,7 @@ impl<'tcx> MirPass<'tcx> for EarlyOtherwiseBranch {
         // Since this optimization adds new basic blocks and invalidates others,
         // clean up the cfg to make it nicer for other passes
         if should_cleanup {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index 17916e16daf..446f13feff0 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -67,11 +67,11 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for RequiresUnsafe {
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::mir_transform_requires_unsafe);
         diag.code(rustc_errors::DiagnosticId::Error("E0133".to_string()));
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag.span_label(self.span, self.details.label());
         let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
-        diag.set_arg("details", desc);
-        diag.set_arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
+        diag.arg("details", desc);
+        diag.arg("op_in_unsafe_fn_allowed", self.op_in_unsafe_fn_allowed);
         self.details.add_subdiagnostics(&mut diag);
         if let Some(sp) = self.enclosing {
             diag.span_label(sp, fluent::mir_transform_not_inherited);
@@ -122,16 +122,16 @@ impl RequiresUnsafeDetail {
             }
             CallToFunctionWith { ref missing, ref build_enabled } => {
                 diag.help(fluent::mir_transform_target_feature_call_help);
-                diag.set_arg(
+                diag.arg(
                     "missing_target_features",
                     DiagnosticArgValue::StrListSepByAnd(
                         missing.iter().map(|feature| Cow::from(feature.as_str())).collect(),
                     ),
                 );
-                diag.set_arg("missing_target_features_count", missing.len());
+                diag.arg("missing_target_features_count", missing.len());
                 if !build_enabled.is_empty() {
                     diag.note(fluent::mir_transform_target_feature_call_note);
-                    diag.set_arg(
+                    diag.arg(
                         "build_target_features",
                         DiagnosticArgValue::StrListSepByAnd(
                             build_enabled
@@ -140,7 +140,7 @@ impl RequiresUnsafeDetail {
                                 .collect(),
                         ),
                     );
-                    diag.set_arg("build_target_features_count", build_enabled.len());
+                    diag.arg("build_target_features_count", build_enabled.len());
                 }
             }
         }
@@ -181,9 +181,8 @@ pub(crate) struct UnsafeOpInUnsafeFn {
 impl<'a> DecorateLint<'a, ()> for UnsafeOpInUnsafeFn {
     #[track_caller]
     fn decorate_lint<'b>(self, diag: &'b mut DiagnosticBuilder<'a, ()>) {
-        let dcx = diag.dcx().expect("lint should not yet be emitted");
-        let desc = dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
-        diag.set_arg("details", desc);
+        let desc = diag.dcx.eagerly_translate_to_string(self.details.label(), [].into_iter());
+        diag.arg("details", desc);
         diag.span_label(self.details.span, self.details.label());
         self.details.add_subdiagnostics(diag);
 
@@ -213,7 +212,7 @@ impl<'a, P: std::fmt::Debug> DecorateLint<'a, ()> for AssertLint<P> {
         let assert_kind = self.panic();
         let message = assert_kind.diagnostic_message();
         assert_kind.add_args(&mut |name, value| {
-            diag.set_arg(name, value);
+            diag.arg(name, value);
         });
         diag.span_label(span, message);
     }
@@ -280,9 +279,9 @@ impl<'a> DecorateLint<'a, ()> for MustNotSupend<'_, '_> {
             diag.subdiagnostic(reason);
         }
         diag.span_help(self.src_sp, fluent::_subdiag::help);
-        diag.set_arg("pre", self.pre);
-        diag.set_arg("def_path", self.tcx.def_path_str(self.def_id));
-        diag.set_arg("post", self.post);
+        diag.arg("pre", self.pre);
+        diag.arg("def_path", self.tcx.def_path_str(self.def_id));
+        diag.arg("post", self.post);
     }
 
     fn msg(&self) -> rustc_errors::DiagnosticMessage {
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 8ad804bf3e7..ebefc3b47cc 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -15,7 +15,7 @@ use rustc_target::abi::FieldIdx;
 use rustc_target::spec::abi::Abi;
 
 use crate::cost_checker::CostChecker;
-use crate::simplify::{remove_dead_blocks, CfgSimplifier};
+use crate::simplify::simplify_cfg;
 use crate::util;
 use std::iter;
 use std::ops::{Range, RangeFrom};
@@ -56,8 +56,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
         let _guard = span.enter();
         if inline(tcx, body) {
             debug!("running simplify cfg on {:?}", body.source);
-            CfgSimplifier::new(body).simplify();
-            remove_dead_blocks(body);
+            simplify_cfg(body);
             deref_finder(tcx, body);
         }
     }
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index a41d8e21245..dcab124505e 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -43,6 +43,7 @@ use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem};
+use rustc_target::abi::{TagEncoding, Variants};
 
 use crate::cost_checker::CostChecker;
 
@@ -391,8 +392,25 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             StatementKind::SetDiscriminant { box place, variant_index } => {
                 let discr_target = self.map.find_discr(place.as_ref())?;
                 let enum_ty = place.ty(self.body, self.tcx).ty;
-                let discr = discriminant_for_variant(enum_ty, *variant_index)?;
-                self.process_operand(bb, discr_target, &discr, state)?;
+                // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
+                // of a niche encoding. If we cannot ensure that we write to the discriminant, do
+                // nothing.
+                let enum_layout = self.tcx.layout_of(self.param_env.and(enum_ty)).ok()?;
+                let writes_discriminant = match enum_layout.variants {
+                    Variants::Single { index } => {
+                        assert_eq!(index, *variant_index);
+                        true
+                    }
+                    Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true,
+                    Variants::Multiple {
+                        tag_encoding: TagEncoding::Niche { untagged_variant, .. },
+                        ..
+                    } => *variant_index != untagged_variant,
+                };
+                if writes_discriminant {
+                    let discr = discriminant_for_variant(enum_ty, *variant_index)?;
+                    self.process_operand(bb, discr_target, &discr, state)?;
+                }
             }
             // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
             StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 5562ae7f3bd..ce9043ec287 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)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
+#![feature(assert_matches)]
 #![feature(box_patterns)]
 #![feature(cow_is_borrowed)]
 #![feature(decl_macro)]
@@ -10,6 +10,7 @@
 #![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(option_get_or_insert_default)]
+#![feature(round_char_boundary)]
 #![feature(trusted_step)]
 #![feature(try_blocks)]
 #![feature(yeet_expr)]
@@ -94,6 +95,7 @@ mod multiple_return_terminators;
 mod normalize_array_len;
 mod nrvo;
 mod prettify;
+mod promote_consts;
 mod ref_prop;
 mod remove_noop_landing_pads;
 mod remove_storage_markers;
@@ -115,7 +117,6 @@ mod uninhabited_enum_branching;
 mod unreachable_prop;
 
 use rustc_const_eval::transform::check_consts::{self, ConstCx};
-use rustc_const_eval::transform::promote_consts;
 use rustc_const_eval::transform::validate;
 use rustc_mir_dataflow::rustc_peek;
 
@@ -285,9 +286,9 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
 /// FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query).
 /// We used to have this for pre-miri MIR based const eval.
 fn mir_const(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
-    // Unsafety check uses the raw mir, so make sure it is run.
+    // MIR unsafety check uses the raw mir, so make sure it is run.
     if !tcx.sess.opts.unstable_opts.thir_unsafeck {
-        tcx.ensure_with_value().unsafety_check_result(def);
+        tcx.ensure_with_value().mir_unsafety_check_result(def);
     }
 
     // has_ffi_unwind_calls query uses the raw mir, so make sure it is run.
@@ -565,17 +566,28 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         body,
         &[
             &check_alignment::CheckAlignment,
-            &lower_slice_len::LowerSliceLenCalls, // has to be done before inlining, otherwise actual call will be almost always inlined. Also simple, so can just do first
+            // Before inlining: trim down MIR with passes to reduce inlining work.
+
+            // Has to be done before inlining, otherwise actual call will be almost always inlined.
+            // Also simple, so can just do first
+            &lower_slice_len::LowerSliceLenCalls,
+            // Perform inlining, which may add a lot of code.
             &inline::Inline,
-            // Substitutions during inlining may introduce switch on enums with uninhabited branches.
+            // Code from other crates may have storage markers, so this needs to happen after inlining.
+            &remove_storage_markers::RemoveStorageMarkers,
+            // Inlining and substitution may introduce ZST and useless drops.
+            &remove_zsts::RemoveZsts,
+            &remove_unneeded_drops::RemoveUnneededDrops,
+            // Type substitution may create uninhabited enums.
             &uninhabited_enum_branching::UninhabitedEnumBranching,
             &unreachable_prop::UnreachablePropagation,
             &o1(simplify::SimplifyCfg::AfterUninhabitedEnumBranching),
-            &remove_storage_markers::RemoveStorageMarkers,
-            &remove_zsts::RemoveZsts,
-            &normalize_array_len::NormalizeArrayLen, // has to run after `slice::len` lowering
+            // Inlining may have introduced a lot of redundant code and a large move pattern.
+            // Now, we need to shrink the generated MIR.
+
+            // Has to run after `slice::len` lowering
+            &normalize_array_len::NormalizeArrayLen,
             &const_goto::ConstGoto,
-            &remove_unneeded_drops::RemoveUnneededDrops,
             &ref_prop::ReferencePropagation,
             &sroa::ScalarReplacementOfAggregates,
             &match_branches::MatchBranchSimplification,
diff --git a/compiler/rustc_mir_transform/src/lint.rs b/compiler/rustc_mir_transform/src/lint.rs
index 3940d0ddbf3..c0c0a3f5ee6 100644
--- a/compiler/rustc_mir_transform/src/lint.rs
+++ b/compiler/rustc_mir_transform/src/lint.rs
@@ -1,6 +1,7 @@
 //! This pass statically detects code which has undefined behaviour or is likely to be erroneous.
 //! It can be used to locate problems in MIR building or optimizations. It assumes that all code
 //! can be executed, so it has false positives.
+use rustc_data_structures::fx::FxHashSet;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::visit::{PlaceContext, Visitor};
 use rustc_middle::mir::*;
@@ -11,7 +12,6 @@ use rustc_mir_dataflow::{Analysis, ResultsCursor};
 use std::borrow::Cow;
 
 pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
-    let reachable_blocks = traversal::reachable_as_bitset(body);
     let always_live_locals = &always_storage_live_locals(body);
 
     let maybe_storage_live = MaybeStorageLive::new(Cow::Borrowed(always_live_locals))
@@ -24,17 +24,19 @@ pub fn lint_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, when: String) {
         .iterate_to_fixpoint()
         .into_results_cursor(body);
 
-    Lint {
+    let mut lint = Lint {
         tcx,
         when,
         body,
         is_fn_like: tcx.def_kind(body.source.def_id()).is_fn_like(),
         always_live_locals,
-        reachable_blocks,
         maybe_storage_live,
         maybe_storage_dead,
+        places: Default::default(),
+    };
+    for (bb, data) in traversal::reachable(body) {
+        lint.visit_basic_block_data(bb, data);
     }
-    .visit_body(body);
 }
 
 struct Lint<'a, 'tcx> {
@@ -43,9 +45,9 @@ struct Lint<'a, 'tcx> {
     body: &'a Body<'tcx>,
     is_fn_like: bool,
     always_live_locals: &'a BitSet<Local>,
-    reachable_blocks: BitSet<BasicBlock>,
     maybe_storage_live: ResultsCursor<'a, 'tcx, MaybeStorageLive<'a>>,
     maybe_storage_dead: ResultsCursor<'a, 'tcx, MaybeStorageDead<'a>>,
+    places: FxHashSet<PlaceRef<'tcx>>,
 }
 
 impl<'a, 'tcx> Lint<'a, 'tcx> {
@@ -67,7 +69,7 @@ impl<'a, 'tcx> Lint<'a, 'tcx> {
 
 impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
     fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
-        if self.reachable_blocks.contains(location.block) && context.is_use() {
+        if context.is_use() {
             self.maybe_storage_dead.seek_after_primary_effect(location);
             if self.maybe_storage_dead.get().contains(local) {
                 self.fail(location, format!("use of local {local:?}, which has no storage here"));
@@ -76,18 +78,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
     }
 
     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
-        match statement.kind {
-            StatementKind::StorageLive(local) => {
-                if self.reachable_blocks.contains(location.block) {
-                    self.maybe_storage_live.seek_before_primary_effect(location);
-                    if self.maybe_storage_live.get().contains(local) {
+        match &statement.kind {
+            StatementKind::Assign(box (dest, rvalue)) => {
+                if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
+                    // The sides of an assignment must not alias. Currently this just checks whether
+                    // the places are identical.
+                    if dest == src {
                         self.fail(
                             location,
-                            format!("StorageLive({local:?}) which already has storage here"),
+                            "encountered `Assign` statement with overlapping memory",
                         );
                     }
                 }
             }
+            StatementKind::StorageLive(local) => {
+                self.maybe_storage_live.seek_before_primary_effect(location);
+                if self.maybe_storage_live.get().contains(*local) {
+                    self.fail(
+                        location,
+                        format!("StorageLive({local:?}) which already has storage here"),
+                    );
+                }
+            }
             _ => {}
         }
 
@@ -95,9 +107,9 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
     }
 
     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
-        match terminator.kind {
+        match &terminator.kind {
             TerminatorKind::Return => {
-                if self.is_fn_like && self.reachable_blocks.contains(location.block) {
+                if self.is_fn_like {
                     self.maybe_storage_live.seek_after_primary_effect(location);
                     for local in self.maybe_storage_live.get().iter() {
                         if !self.always_live_locals.contains(local) {
@@ -111,6 +123,28 @@ impl<'a, 'tcx> Visitor<'tcx> for Lint<'a, 'tcx> {
                     }
                 }
             }
+            TerminatorKind::Call { args, destination, .. } => {
+                // The call destination place and Operand::Move place used as an argument might be
+                // passed by a reference to the callee. Consequently they must be non-overlapping.
+                // Currently this simply checks for duplicate places.
+                self.places.clear();
+                self.places.insert(destination.as_ref());
+                let mut has_duplicates = false;
+                for arg in args {
+                    if let Operand::Move(place) = arg {
+                        has_duplicates |= !self.places.insert(place.as_ref());
+                    }
+                }
+                if has_duplicates {
+                    self.fail(
+                        location,
+                        format!(
+                            "encountered overlapping memory in `Move` arguments to `Call` terminator: {:?}",
+                            terminator.kind,
+                        ),
+                    );
+                }
+            }
             _ => {}
         }
 
diff --git a/compiler/rustc_mir_transform/src/match_branches.rs b/compiler/rustc_mir_transform/src/match_branches.rs
index 1c4aa37d57f..6d4332793af 100644
--- a/compiler/rustc_mir_transform/src/match_branches.rs
+++ b/compiler/rustc_mir_transform/src/match_branches.rs
@@ -174,7 +174,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
         }
 
         if should_cleanup {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs
index 82074f1960d..f4c572aec12 100644
--- a/compiler/rustc_mir_transform/src/pass_manager.rs
+++ b/compiler/rustc_mir_transform/src/pass_manager.rs
@@ -109,14 +109,15 @@ fn run_passes_inner<'tcx>(
     phase_change: Option<MirPhase>,
     validate_each: bool,
 ) {
-    let lint = tcx.sess.opts.unstable_opts.lint_mir & !body.should_skip();
-    let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir & !body.should_skip();
     let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
     trace!(?overridden_passes);
 
     let prof_arg = tcx.sess.prof.enabled().then(|| format!("{:?}", body.source.def_id()));
 
     if !body.should_skip() {
+        let validate = validate_each & tcx.sess.opts.unstable_opts.validate_mir;
+        let lint = tcx.sess.opts.unstable_opts.lint_mir;
+
         for pass in passes {
             let name = pass.name();
 
@@ -162,7 +163,12 @@ fn run_passes_inner<'tcx>(
         body.pass_count = 0;
 
         dump_mir_for_phase_change(tcx, body);
-        if validate || new_phase == MirPhase::Runtime(RuntimePhase::Optimized) {
+
+        let validate =
+            (validate_each & tcx.sess.opts.unstable_opts.validate_mir & !body.should_skip())
+                || new_phase == MirPhase::Runtime(RuntimePhase::Optimized);
+        let lint = tcx.sess.opts.unstable_opts.lint_mir & !body.should_skip();
+        if validate {
             validate_body(tcx, body, format!("after phase change to {}", new_phase.name()));
         }
         if lint {
diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 8b2ea2dc21d..841b86fed45 100644
--- a/compiler/rustc_const_eval/src/transform/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -12,6 +12,7 @@
 //! initialization and can otherwise silence errors, if
 //! move analysis runs after promotion on broken MIR.
 
+use either::{Left, Right};
 use rustc_hir as hir;
 use rustc_middle::mir;
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
@@ -22,10 +23,11 @@ use rustc_span::Span;
 
 use rustc_index::{Idx, IndexSlice, IndexVec};
 
+use std::assert_matches::assert_matches;
 use std::cell::Cell;
 use std::{cmp, iter, mem};
 
-use crate::transform::check_consts::{qualifs, ConstCx};
+use rustc_const_eval::transform::check_consts::{qualifs, ConstCx};
 
 /// A `MirPass` for promotion.
 ///
@@ -64,7 +66,7 @@ impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> {
 
 /// State of a temporary during collection and promotion.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum TempState {
+enum TempState {
     /// No references to this temp.
     Undefined,
     /// One direct assignment and any number of direct uses.
@@ -78,18 +80,11 @@ pub enum TempState {
     PromotedOut,
 }
 
-impl TempState {
-    pub fn is_promotable(&self) -> bool {
-        debug!("is_promotable: self={:?}", self);
-        matches!(self, TempState::Defined { .. })
-    }
-}
-
 /// A "root candidate" for promotion, which will become the
 /// returned value in a promoted MIR, unless it's a subset
 /// of a larger candidate.
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub struct Candidate {
+struct Candidate {
     location: Location,
 }
 
@@ -123,46 +118,43 @@ impl<'tcx> Visitor<'tcx> for Collector<'_, 'tcx> {
 
         let temp = &mut self.temps[index];
         debug!("visit_local: temp={:?}", temp);
-        if *temp == TempState::Undefined {
-            match context {
+        *temp = match *temp {
+            TempState::Undefined => match context {
                 PlaceContext::MutatingUse(MutatingUseContext::Store)
                 | PlaceContext::MutatingUse(MutatingUseContext::Call) => {
-                    *temp = TempState::Defined { location, uses: 0, valid: Err(()) };
+                    TempState::Defined { location, uses: 0, valid: Err(()) }
+                }
+                _ => TempState::Unpromotable,
+            },
+            TempState::Defined { ref mut uses, .. } => {
+                // We always allow borrows, even mutable ones, as we need
+                // to promote mutable borrows of some ZSTs e.g., `&mut []`.
+                let allowed_use = match context {
+                    PlaceContext::MutatingUse(MutatingUseContext::Borrow)
+                    | PlaceContext::NonMutatingUse(_) => true,
+                    PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false,
+                };
+                debug!("visit_local: allowed_use={:?}", allowed_use);
+                if allowed_use {
+                    *uses += 1;
                     return;
                 }
-                _ => { /* mark as unpromotable below */ }
+                TempState::Unpromotable
             }
-        } else if let TempState::Defined { uses, .. } = temp {
-            // We always allow borrows, even mutable ones, as we need
-            // to promote mutable borrows of some ZSTs e.g., `&mut []`.
-            let allowed_use = match context {
-                PlaceContext::MutatingUse(MutatingUseContext::Borrow)
-                | PlaceContext::NonMutatingUse(_) => true,
-                PlaceContext::MutatingUse(_) | PlaceContext::NonUse(_) => false,
-            };
-            debug!("visit_local: allowed_use={:?}", allowed_use);
-            if allowed_use {
-                *uses += 1;
-                return;
-            }
-            /* mark as unpromotable below */
-        }
-        *temp = TempState::Unpromotable;
+            TempState::Unpromotable | TempState::PromotedOut => TempState::Unpromotable,
+        };
     }
 
     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
         self.super_rvalue(rvalue, location);
 
-        match *rvalue {
-            Rvalue::Ref(..) => {
-                self.candidates.push(Candidate { location });
-            }
-            _ => {}
+        if let Rvalue::Ref(..) = *rvalue {
+            self.candidates.push(Candidate { location });
         }
     }
 }
 
-pub fn collect_temps_and_candidates<'tcx>(
+fn collect_temps_and_candidates<'tcx>(
     ccx: &ConstCx<'_, 'tcx>,
 ) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
     let mut collector = Collector {
@@ -196,230 +188,165 @@ struct Unpromotable;
 
 impl<'tcx> Validator<'_, 'tcx> {
     fn validate_candidate(&mut self, candidate: Candidate) -> Result<(), Unpromotable> {
-        let loc = candidate.location;
-        let statement = &self.body[loc.block].statements[loc.statement_index];
-        match &statement.kind {
-            StatementKind::Assign(box (_, Rvalue::Ref(_, kind, place))) => {
-                // We can only promote interior borrows of promotable temps (non-temps
-                // don't get promoted anyway).
-                self.validate_local(place.local)?;
-
-                // The reference operation itself must be promotable.
-                // (Needs to come after `validate_local` to avoid ICEs.)
-                self.validate_ref(*kind, place)?;
+        let Left(statement) = self.body.stmt_at(candidate.location) else { bug!() };
+        let Some((_, Rvalue::Ref(_, kind, place))) = statement.kind.as_assign() else { bug!() };
 
-                // We do not check all the projections (they do not get promoted anyway),
-                // but we do stay away from promoting anything involving a dereference.
-                if place.projection.contains(&ProjectionElem::Deref) {
-                    return Err(Unpromotable);
-                }
+        // We can only promote interior borrows of promotable temps (non-temps
+        // don't get promoted anyway).
+        self.validate_local(place.local)?;
 
-                Ok(())
-            }
-            _ => bug!(),
+        // The reference operation itself must be promotable.
+        // (Needs to come after `validate_local` to avoid ICEs.)
+        self.validate_ref(*kind, place)?;
+
+        // We do not check all the projections (they do not get promoted anyway),
+        // but we do stay away from promoting anything involving a dereference.
+        if place.projection.contains(&ProjectionElem::Deref) {
+            return Err(Unpromotable);
         }
+
+        Ok(())
     }
 
     // FIXME(eddyb) maybe cache this?
     fn qualif_local<Q: qualifs::Qualif>(&mut self, local: Local) -> bool {
-        if let TempState::Defined { location: loc, .. } = self.temps[local] {
-            let num_stmts = self.body[loc.block].statements.len();
-
-            if loc.statement_index < num_stmts {
-                let statement = &self.body[loc.block].statements[loc.statement_index];
-                match &statement.kind {
-                    StatementKind::Assign(box (_, rhs)) => qualifs::in_rvalue::<Q, _>(
-                        self.ccx,
-                        &mut |l| self.qualif_local::<Q>(l),
-                        rhs,
-                    ),
-                    _ => {
+        let TempState::Defined { location: loc, .. } = self.temps[local] else {
+            return false;
+        };
+
+        let stmt_or_term = self.body.stmt_at(loc);
+        match stmt_or_term {
+            Left(statement) => {
+                let Some((_, rhs)) = statement.kind.as_assign() else {
+                    span_bug!(statement.source_info.span, "{:?} is not an assignment", statement)
+                };
+                qualifs::in_rvalue::<Q, _>(self.ccx, &mut |l| self.qualif_local::<Q>(l), rhs)
+            }
+            Right(terminator) => {
+                assert_matches!(terminator.kind, TerminatorKind::Call { .. });
+                let return_ty = self.body.local_decls[local].ty;
+                Q::in_any_value_of_ty(self.ccx, return_ty)
+            }
+        }
+    }
+
+    fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
+        let TempState::Defined { location: loc, uses, valid } = self.temps[local] else {
+            return Err(Unpromotable);
+        };
+
+        // We cannot promote things that need dropping, since the promoted value would not get
+        // dropped.
+        if self.qualif_local::<qualifs::NeedsDrop>(local) {
+            return Err(Unpromotable);
+        }
+
+        if valid.is_ok() {
+            return Ok(());
+        }
+
+        let ok = {
+            let stmt_or_term = self.body.stmt_at(loc);
+            match stmt_or_term {
+                Left(statement) => {
+                    let Some((_, rhs)) = statement.kind.as_assign() else {
                         span_bug!(
                             statement.source_info.span,
                             "{:?} is not an assignment",
                             statement
-                        );
-                    }
+                        )
+                    };
+                    self.validate_rvalue(rhs)
                 }
-            } else {
-                let terminator = self.body[loc.block].terminator();
-                match &terminator.kind {
-                    TerminatorKind::Call { .. } => {
-                        let return_ty = self.body.local_decls[local].ty;
-                        Q::in_any_value_of_ty(self.ccx, return_ty)
-                    }
+                Right(terminator) => match &terminator.kind {
+                    TerminatorKind::Call { func, args, .. } => self.validate_call(func, args),
+                    TerminatorKind::Yield { .. } => Err(Unpromotable),
                     kind => {
                         span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                     }
-                }
+                },
             }
-        } else {
-            false
-        }
-    }
+        };
 
-    fn validate_local(&mut self, local: Local) -> Result<(), Unpromotable> {
-        if let TempState::Defined { location: loc, uses, valid } = self.temps[local] {
-            // We cannot promote things that need dropping, since the promoted value
-            // would not get dropped.
-            if self.qualif_local::<qualifs::NeedsDrop>(local) {
-                return Err(Unpromotable);
-            }
-            valid.or_else(|_| {
-                let ok = {
-                    let block = &self.body[loc.block];
-                    let num_stmts = block.statements.len();
-
-                    if loc.statement_index < num_stmts {
-                        let statement = &block.statements[loc.statement_index];
-                        match &statement.kind {
-                            StatementKind::Assign(box (_, rhs)) => self.validate_rvalue(rhs),
-                            _ => {
-                                span_bug!(
-                                    statement.source_info.span,
-                                    "{:?} is not an assignment",
-                                    statement
-                                );
-                            }
-                        }
-                    } else {
-                        let terminator = block.terminator();
-                        match &terminator.kind {
-                            TerminatorKind::Call { func, args, .. } => {
-                                self.validate_call(func, args)
-                            }
-                            TerminatorKind::Yield { .. } => Err(Unpromotable),
-                            kind => {
-                                span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
-                            }
-                        }
-                    }
-                };
-                self.temps[local] = match ok {
-                    Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) },
-                    Err(_) => TempState::Unpromotable,
-                };
-                ok
-            })
-        } else {
-            Err(Unpromotable)
-        }
+        self.temps[local] = match ok {
+            Ok(()) => TempState::Defined { location: loc, uses, valid: Ok(()) },
+            Err(_) => TempState::Unpromotable,
+        };
+
+        ok
     }
 
     fn validate_place(&mut self, place: PlaceRef<'tcx>) -> Result<(), Unpromotable> {
-        match place.last_projection() {
-            None => self.validate_local(place.local),
-            Some((place_base, elem)) => {
-                // Validate topmost projection, then recurse.
-                match elem {
-                    ProjectionElem::Deref => {
-                        let mut promotable = false;
-                        // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
-                        // and we need to be able to promote this. So check if this deref matches
-                        // that specific pattern.
-
-                        // We need to make sure this is a `Deref` of a local with no further projections.
-                        // Discussion can be found at
-                        // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
-                        if let Some(local) = place_base.as_local() {
-                            if let TempState::Defined { location, .. } = self.temps[local] {
-                                let def_stmt = self.body[location.block]
-                                    .statements
-                                    .get(location.statement_index);
-                                if let Some(Statement {
-                                    kind:
-                                        StatementKind::Assign(box (
-                                            _,
-                                            Rvalue::Use(Operand::Constant(c)),
-                                        )),
-                                    ..
-                                }) = def_stmt
-                                {
-                                    if let Some(did) = c.check_static_ptr(self.tcx) {
-                                        // Evaluating a promoted may not read statics except if it got
-                                        // promoted from a static (this is a CTFE check). So we
-                                        // can only promote static accesses inside statics.
-                                        if let Some(hir::ConstContext::Static(..)) = self.const_kind
-                                        {
-                                            if !self.tcx.is_thread_local_static(did) {
-                                                promotable = true;
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                        if !promotable {
-                            return Err(Unpromotable);
-                        }
-                    }
-                    ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
-                        return Err(Unpromotable);
-                    }
+        let Some((place_base, elem)) = place.last_projection() else {
+            return self.validate_local(place.local);
+        };
 
-                    ProjectionElem::ConstantIndex { .. }
-                    | ProjectionElem::Subtype(_)
-                    | ProjectionElem::Subslice { .. } => {}
-
-                    ProjectionElem::Index(local) => {
-                        let mut promotable = false;
-                        // Only accept if we can predict the index and are indexing an array.
-                        let val = if let TempState::Defined { location: loc, .. } =
-                            self.temps[local]
-                        {
-                            let block = &self.body[loc.block];
-                            if loc.statement_index < block.statements.len() {
-                                let statement = &block.statements[loc.statement_index];
-                                match &statement.kind {
-                                    StatementKind::Assign(box (
-                                        _,
-                                        Rvalue::Use(Operand::Constant(c)),
-                                    )) => c.const_.try_eval_target_usize(self.tcx, self.param_env),
-                                    _ => None,
-                                }
-                            } else {
-                                None
-                            }
-                        } else {
-                            None
-                        };
-                        if let Some(idx) = val {
-                            // Determine the type of the thing we are indexing.
-                            let ty = place_base.ty(self.body, self.tcx).ty;
-                            match ty.kind() {
-                                ty::Array(_, len) => {
-                                    // It's an array; determine its length.
-                                    if let Some(len) =
-                                        len.try_eval_target_usize(self.tcx, self.param_env)
-                                    {
-                                        // If the index is in-bounds, go ahead.
-                                        if idx < len {
-                                            promotable = true;
-                                        }
-                                    }
-                                }
-                                _ => {}
-                            }
-                        }
-                        if !promotable {
-                            return Err(Unpromotable);
-                        }
+        // Validate topmost projection, then recurse.
+        match elem {
+            // Recurse directly.
+            ProjectionElem::ConstantIndex { .. }
+            | ProjectionElem::Subtype(_)
+            | ProjectionElem::Subslice { .. } => {}
 
-                        self.validate_local(local)?;
-                    }
+            // Never recurse.
+            ProjectionElem::OpaqueCast(..) | ProjectionElem::Downcast(..) => {
+                return Err(Unpromotable);
+            }
 
-                    ProjectionElem::Field(..) => {
-                        let base_ty = place_base.ty(self.body, self.tcx).ty;
-                        if base_ty.is_union() {
-                            // No promotion of union field accesses.
-                            return Err(Unpromotable);
-                        }
-                    }
+            ProjectionElem::Deref => {
+                // When a static is used by-value, that gets desugared to `*STATIC_ADDR`,
+                // and we need to be able to promote this. So check if this deref matches
+                // that specific pattern.
+
+                // We need to make sure this is a `Deref` of a local with no further projections.
+                // Discussion can be found at
+                // https://github.com/rust-lang/rust/pull/74945#discussion_r463063247
+                if let Some(local) = place_base.as_local()
+                    && let TempState::Defined { location, .. } = self.temps[local]
+                    && let Left(def_stmt) = self.body.stmt_at(location)
+                    && let Some((_, Rvalue::Use(Operand::Constant(c)))) = def_stmt.kind.as_assign()
+                    && let Some(did) = c.check_static_ptr(self.tcx)
+                    // Evaluating a promoted may not read statics except if it got
+                    // promoted from a static (this is a CTFE check). So we
+                    // can only promote static accesses inside statics.
+                    && let Some(hir::ConstContext::Static(..)) = self.const_kind
+                    && !self.tcx.is_thread_local_static(did)
+                {
+                    // Recurse.
+                } else {
+                    return Err(Unpromotable);
                 }
+            }
+            ProjectionElem::Index(local) => {
+                // Only accept if we can predict the index and are indexing an array.
+                if let TempState::Defined { location: loc, .. } = self.temps[local]
+                    && let Left(statement) =  self.body.stmt_at(loc)
+                    && let Some((_, Rvalue::Use(Operand::Constant(c)))) = statement.kind.as_assign()
+                    && let Some(idx) = c.const_.try_eval_target_usize(self.tcx, self.param_env)
+                    // Determine the type of the thing we are indexing.
+                    && let ty::Array(_, len) = place_base.ty(self.body, self.tcx).ty.kind()
+                    // It's an array; determine its length.
+                    && let Some(len) = len.try_eval_target_usize(self.tcx, self.param_env)
+                    // If the index is in-bounds, go ahead.
+                    && idx < len
+                {
+                    self.validate_local(local)?;
+                    // Recurse.
+                } else {
+                    return Err(Unpromotable);
+                }
+            }
 
-                self.validate_place(place_base)
+            ProjectionElem::Field(..) => {
+                let base_ty = place_base.ty(self.body, self.tcx).ty;
+                if base_ty.is_union() {
+                    // No promotion of union field accesses.
+                    return Err(Unpromotable);
+                }
             }
         }
+
+        self.validate_place(place_base)
     }
 
     fn validate_operand(&mut self, operand: &Operand<'tcx>) -> Result<(), Unpromotable> {
@@ -676,7 +603,7 @@ impl<'tcx> Validator<'_, 'tcx> {
 }
 
 // FIXME(eddyb) remove the differences for promotability in `static`, `const`, `const fn`.
-pub fn validate_candidates(
+fn validate_candidates(
     ccx: &ConstCx<'_, '_>,
     temps: &mut IndexSlice<Local, TempState>,
     candidates: &[Candidate],
@@ -930,7 +857,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
     }
 }
 
-pub fn promote_candidates<'tcx>(
+fn promote_candidates<'tcx>(
     body: &mut Body<'tcx>,
     tcx: TyCtxt<'tcx>,
     mut temps: IndexVec<Local, TempState>,
@@ -969,7 +896,7 @@ pub fn promote_candidates<'tcx>(
             0,
             vec![],
             body.span,
-            body.coroutine_kind(),
+            None,
             body.tainted_by_errors,
         );
         promoted.phase = MirPhase::Analysis(AnalysisPhase::Initial);
diff --git a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
index 5d528bed356..2778d91e17b 100644
--- a/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
+++ b/compiler/rustc_mir_transform/src/remove_unneeded_drops.rs
@@ -38,7 +38,7 @@ impl<'tcx> MirPass<'tcx> for RemoveUnneededDrops {
         // if we applied optimizations, we potentially have some cfg to cleanup to
         // make it easier for further passes
         if should_simplify {
-            simplify_cfg(tcx, body);
+            simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/separate_const_switch.rs b/compiler/rustc_mir_transform/src/separate_const_switch.rs
index 6e22690d8da..7120ef72142 100644
--- a/compiler/rustc_mir_transform/src/separate_const_switch.rs
+++ b/compiler/rustc_mir_transform/src/separate_const_switch.rs
@@ -50,11 +50,11 @@ impl<'tcx> MirPass<'tcx> for SeparateConstSwitch {
         sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         // If execution did something, applying a simplification layer
         // helps later passes optimize the copy away.
         if separate_const_switch(body) > 0 {
-            super::simplify::simplify_cfg(tcx, body);
+            super::simplify::simplify_cfg(body);
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/simplify.rs b/compiler/rustc_mir_transform/src/simplify.rs
index 856a0f22771..8c8818bd68e 100644
--- a/compiler/rustc_mir_transform/src/simplify.rs
+++ b/compiler/rustc_mir_transform/src/simplify.rs
@@ -27,7 +27,6 @@
 //! naively generate still contains the `_a = ()` write in the unreachable block "after" the
 //! return.
 
-use rustc_data_structures::fx::FxIndexSet;
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
@@ -62,9 +61,8 @@ impl SimplifyCfg {
     }
 }
 
-pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+pub(crate) fn simplify_cfg(body: &mut Body<'_>) {
     CfgSimplifier::new(body).simplify();
-    remove_duplicate_unreachable_blocks(tcx, body);
     remove_dead_blocks(body);
 
     // FIXME: Should probably be moved into some kind of pass manager
@@ -76,9 +74,9 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
         self.name()
     }
 
-    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+    fn run_pass(&self, _: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         debug!("SimplifyCfg({:?}) - simplifying {:?}", self.name(), body.source);
-        simplify_cfg(tcx, body);
+        simplify_cfg(body);
     }
 }
 
@@ -289,55 +287,25 @@ pub fn simplify_duplicate_switch_targets(terminator: &mut Terminator<'_>) {
     }
 }
 
-pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
-    struct OptApplier<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        duplicates: FxIndexSet<BasicBlock>,
-    }
-
-    impl<'tcx> MutVisitor<'tcx> for OptApplier<'tcx> {
-        fn tcx(&self) -> TyCtxt<'tcx> {
-            self.tcx
-        }
-
-        fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
-            for target in terminator.successors_mut() {
-                // We don't have to check whether `target` is a cleanup block, because have
-                // entirely excluded cleanup blocks in building the set of duplicates.
-                if self.duplicates.contains(target) {
-                    *target = self.duplicates[0];
-                }
-            }
-
-            simplify_duplicate_switch_targets(terminator);
-
-            self.super_terminator(terminator, location);
-        }
-    }
+pub(crate) fn remove_dead_blocks(body: &mut Body<'_>) {
+    let should_deduplicate_unreachable = |bbdata: &BasicBlockData<'_>| {
+        // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
+        // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
+        // before then so we need to handle missing terminators.
+        // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
+        // don't emit empty unreachable cleanup blocks, so this simple check suffices.
+        bbdata.terminator.is_some() && bbdata.is_empty_unreachable() && !bbdata.is_cleanup
+    };
 
-    let unreachable_blocks = body
+    let reachable = traversal::reachable_as_bitset(body);
+    let empty_unreachable_blocks = body
         .basic_blocks
         .iter_enumerated()
-        .filter(|(_, bb)| {
-            // CfgSimplifier::simplify leaves behind some unreachable basic blocks without a
-            // terminator. Those blocks will be deleted by remove_dead_blocks, but we run just
-            // before then so we need to handle missing terminators.
-            // We also need to prevent confusing cleanup and non-cleanup blocks. In practice we
-            // don't emit empty unreachable cleanup blocks, so this simple check suffices.
-            bb.terminator.is_some() && bb.is_empty_unreachable() && !bb.is_cleanup
-        })
-        .map(|(block, _)| block)
-        .collect::<FxIndexSet<_>>();
-
-    if unreachable_blocks.len() > 1 {
-        OptApplier { tcx, duplicates: unreachable_blocks }.visit_body(body);
-    }
-}
+        .filter(|(bb, bbdata)| should_deduplicate_unreachable(bbdata) && reachable.contains(*bb))
+        .count();
 
-pub fn remove_dead_blocks(body: &mut Body<'_>) {
-    let reachable = traversal::reachable_as_bitset(body);
     let num_blocks = body.basic_blocks.len();
-    if num_blocks == reachable.count() {
+    if num_blocks == reachable.count() && empty_unreachable_blocks <= 1 {
         return;
     }
 
@@ -346,14 +314,28 @@ pub fn remove_dead_blocks(body: &mut Body<'_>) {
     let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
     let mut orig_index = 0;
     let mut used_index = 0;
-    basic_blocks.raw.retain(|_| {
-        let keep = reachable.contains(BasicBlock::new(orig_index));
-        if keep {
-            replacements[orig_index] = BasicBlock::new(used_index);
-            used_index += 1;
+    let mut kept_unreachable = None;
+    basic_blocks.raw.retain(|bbdata| {
+        let orig_bb = BasicBlock::new(orig_index);
+        if !reachable.contains(orig_bb) {
+            orig_index += 1;
+            return false;
+        }
+
+        let used_bb = BasicBlock::new(used_index);
+        if should_deduplicate_unreachable(bbdata) {
+            let kept_unreachable = *kept_unreachable.get_or_insert(used_bb);
+            if kept_unreachable != used_bb {
+                replacements[orig_index] = kept_unreachable;
+                orig_index += 1;
+                return false;
+            }
         }
+
+        replacements[orig_index] = used_bb;
+        used_index += 1;
         orig_index += 1;
-        keep
+        true
     });
 
     for block in basic_blocks {
diff --git a/compiler/rustc_mir_transform/src/ssa.rs b/compiler/rustc_mir_transform/src/ssa.rs
index 3a6e1ef3488..1ed3b14e755 100644
--- a/compiler/rustc_mir_transform/src/ssa.rs
+++ b/compiler/rustc_mir_transform/src/ssa.rs
@@ -94,6 +94,7 @@ impl SsaLocals {
         self.direct_uses[local]
     }
 
+    #[inline]
     pub fn assignment_dominates(
         &self,
         dominators: &Dominators<BasicBlock>,
diff --git a/compiler/rustc_mir_transform/src/unreachable_prop.rs b/compiler/rustc_mir_transform/src/unreachable_prop.rs
index f12a6aa2429..bff59aa6023 100644
--- a/compiler/rustc_mir_transform/src/unreachable_prop.rs
+++ b/compiler/rustc_mir_transform/src/unreachable_prop.rs
@@ -54,6 +54,8 @@ impl MirPass<'_> for UnreachablePropagation {
         patch.apply(body);
 
         // We do want do keep some unreachable blocks, but make them empty.
+        // The order in which we clear bb statements does not matter.
+        #[allow(rustc::potential_query_instability)]
         for bb in unreachable_blocks {
             body.basic_blocks_mut()[bb].statements.clear();
         }
diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl
index e27875853df..94b553a07a7 100644
--- a/compiler/rustc_monomorphize/messages.ftl
+++ b/compiler/rustc_monomorphize/messages.ftl
@@ -20,6 +20,9 @@ monomorphize_recursion_limit =
     reached the recursion limit while instantiating `{$shrunk}`
     .note = `{$def_path_str}` defined here
 
+monomorphize_start_not_found = using `fn main` requires the standard library
+    .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`
+
 monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
 
 monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 44beafa0873..92f001cc321 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -194,7 +194,7 @@ use rustc_target::abi::Size;
 use std::path::PathBuf;
 
 use crate::errors::{
-    EncounteredErrorWhileInstantiating, LargeAssignmentsLint, NoOptimizedMir, RecursionLimit,
+    self, EncounteredErrorWhileInstantiating, LargeAssignmentsLint, NoOptimizedMir, RecursionLimit,
     TypeLengthLimit,
 };
 
@@ -1272,7 +1272,9 @@ impl<'v> RootCollector<'_, 'v> {
             return;
         };
 
-        let start_def_id = self.tcx.require_lang_item(LangItem::Start, None);
+        let Some(start_def_id) = self.tcx.lang_items().start_fn() else {
+            self.tcx.dcx().emit_fatal(errors::StartNotFound);
+        };
         let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output();
 
         // Given that `main()` has no arguments,
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index 592e71251b8..bd89874b5cc 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -51,7 +51,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for UnusedGenericParamsHint {
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag =
             DiagnosticBuilder::new(dcx, level, fluent::monomorphize_unused_generic_params);
-        diag.set_span(self.span);
+        diag.span(self.span);
         for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
             // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
             // or a label with a dynamic value in a hard-coded string, but I haven't figured out
@@ -95,6 +95,11 @@ pub struct EncounteredErrorWhileInstantiating {
 }
 
 #[derive(Diagnostic)]
+#[diag(monomorphize_start_not_found)]
+#[help]
+pub struct StartNotFound;
+
+#[derive(Diagnostic)]
 #[diag(monomorphize_unknown_cgu_collection_mode)]
 pub struct UnknownCguCollectionMode<'a> {
     pub mode: &'a str,
diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs
index 08e62840839..2c40cd4d8f2 100644
--- a/compiler/rustc_monomorphize/src/partitioning.rs
+++ b/compiler/rustc_monomorphize/src/partitioning.rs
@@ -1093,7 +1093,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co
                 MonoItemCollectionMode::Eager
             } else {
                 if mode != "lazy" {
-                    tcx.dcx().emit_warning(UnknownCguCollectionMode { mode });
+                    tcx.dcx().emit_warn(UnknownCguCollectionMode { mode });
                 }
 
                 MonoItemCollectionMode::Lazy
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index c6bddbfacd6..c11a6fab7e5 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -10,6 +10,8 @@ parse_ambiguous_range_pattern = the range pattern here has ambiguous interpretat
 parse_array_brackets_instead_of_braces = this is a block expression, not an array
     .suggestion = to make an array, use square brackets instead of curly braces
 
+parse_array_index_offset_of = array indexing not supported in offset_of
+
 parse_assignment_else_not_allowed = <assignment> ... else {"{"} ... {"}"} is not allowed
 
 parse_assoc_lifetime = associated lifetimes are not supported
@@ -405,6 +407,8 @@ parse_invalid_logical_operator = `{$incorrect}` is not a logical operator
 
 parse_invalid_meta_item = expected unsuffixed literal or identifier, found `{$token}`
 
+parse_invalid_offset_of = offset_of expects dot-separated field and variant names
+
 parse_invalid_unicode_escape = invalid unicode character escape
     .label = invalid escape
     .help = unicode escape must {$surrogate ->
@@ -767,6 +771,9 @@ parse_unexpected_if_with_if = unexpected `if` in the condition expression
 parse_unexpected_lifetime_in_pattern = unexpected lifetime `{$symbol}` in pattern
     .suggestion = remove the lifetime
 
+parse_unexpected_paren_in_range_pat = range pattern bounds cannot have parentheses
+parse_unexpected_paren_in_range_pat_sugg = remove these parentheses
+
 parse_unexpected_parentheses_in_for_head = unexpected parentheses surrounding `for` loop head
     .suggestion = remove parentheses in `for` loop
 
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index e276b34ca37..0de252707bd 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1065,8 +1065,8 @@ impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier {
                 None => fluent::parse_expected_identifier_found_str,
             },
         );
-        diag.set_span(self.span);
-        diag.set_arg("token", self.token);
+        diag.span(self.span);
+        diag.arg("token", self.token);
 
         if let Some(sugg) = self.suggest_raw {
             sugg.add_to_diagnostic(&mut diag);
@@ -1123,8 +1123,8 @@ impl<'a> IntoDiagnostic<'a> for ExpectedSemi {
                 None => fluent::parse_expected_semi_found_str,
             },
         );
-        diag.set_span(self.span);
-        diag.set_arg("token", self.token);
+        diag.span(self.span);
+        diag.arg("token", self.token);
 
         if let Some(unexpected_token_label) = self.unexpected_token_label {
             diag.span_label(unexpected_token_label, fluent::parse_label_unexpected_token);
@@ -2379,6 +2379,27 @@ pub(crate) struct ExpectedCommaAfterPatternField {
 }
 
 #[derive(Diagnostic)]
+#[diag(parse_unexpected_paren_in_range_pat)]
+pub(crate) struct UnexpectedParenInRangePat {
+    #[primary_span]
+    pub span: Vec<Span>,
+    #[subdiagnostic]
+    pub sugg: UnexpectedParenInRangePatSugg,
+}
+
+#[derive(Subdiagnostic)]
+#[multipart_suggestion(
+    parse_unexpected_paren_in_range_pat_sugg,
+    applicability = "machine-applicable"
+)]
+pub(crate) struct UnexpectedParenInRangePatSugg {
+    #[suggestion_part(code = "")]
+    pub start_span: Span,
+    #[suggestion_part(code = "")]
+    pub end_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(parse_return_types_use_thin_arrow)]
 pub(crate) struct ReturnTypesUseThinArrow {
     #[primary_span]
@@ -2887,3 +2908,11 @@ pub(crate) struct TransposeDynOrImplSugg<'a> {
     pub insertion_span: Span,
     pub kw: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag(parse_array_index_offset_of)]
+pub(crate) struct ArrayIndexInOffsetOf(#[primary_span] pub Span);
+
+#[derive(Diagnostic)]
+#[diag(parse_invalid_offset_of)]
+pub(crate) struct InvalidOffsetOf(#[primary_span] pub Span);
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 083d1984e00..7db9291921f 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -7,9 +7,7 @@ use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
-use rustc_errors::{
-    error_code, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, FatalAbort, StashKey,
-};
+use rustc_errors::{error_code, Applicability, DiagCtxt, Diagnostic, StashKey};
 use rustc_lexer::unescape::{self, EscapeError, Mode};
 use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_lexer::{Cursor, LiteralKind};
@@ -69,7 +67,7 @@ pub(crate) fn parse_token_trees<'a>(
     let (stream, res, unmatched_delims) =
         tokentrees::TokenTreesReader::parse_all_token_trees(string_reader);
     match res {
-        Ok(_open_spacing) if unmatched_delims.is_empty() => Ok(stream),
+        Ok(()) if unmatched_delims.is_empty() => Ok(stream),
         _ => {
             // Return error if there are unmatched delimiters or unclosed delimiters.
             // We emit delimiter mismatch errors first, then emit the unclosing delimiter mismatch
@@ -251,9 +249,9 @@ impl<'a> StringReader<'a> {
                     let lifetime_name = self.str_from(start);
                     if starts_with_number {
                         let span = self.mk_sp(start, self.pos);
-                        let mut diag = self.dcx().struct_err("lifetimes cannot start with a number");
-                        diag.set_span(span);
-                        diag.stash(span, StashKey::LifetimeIsChar);
+                        self.dcx().struct_err("lifetimes cannot start with a number")
+                            .with_span(span)
+                            .stash(span, StashKey::LifetimeIsChar);
                     }
                     let ident = Symbol::intern(lifetime_name);
                     token::Lifetime(ident)
@@ -344,18 +342,6 @@ impl<'a> StringReader<'a> {
         token::Ident(sym, false)
     }
 
-    fn struct_fatal_span_char(
-        &self,
-        from_pos: BytePos,
-        to_pos: BytePos,
-        m: &str,
-        c: char,
-    ) -> DiagnosticBuilder<'a, FatalAbort> {
-        self.sess
-            .dcx
-            .struct_span_fatal(self.mk_sp(from_pos, to_pos), format!("{}: {}", m, escaped_char(c)))
-    }
-
     /// Detect usages of Unicode codepoints changing the direction of the text on screen and loudly
     /// complain about it.
     fn lint_unicode_text_flow(&self, start: BytePos) {
@@ -409,51 +395,58 @@ impl<'a> StringReader<'a> {
         match kind {
             rustc_lexer::LiteralKind::Char { terminated } => {
                 if !terminated {
-                    self.dcx().span_fatal_with_code(
-                        self.mk_sp(start, end),
-                        "unterminated character literal",
-                        error_code!(E0762),
-                    )
+                    self.dcx()
+                        .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal")
+                        .with_code(error_code!(E0762))
+                        .emit()
                 }
                 self.cook_quoted(token::Char, Mode::Char, start, end, 1, 1) // ' '
             }
             rustc_lexer::LiteralKind::Byte { terminated } => {
                 if !terminated {
-                    self.dcx().span_fatal_with_code(
-                        self.mk_sp(start + BytePos(1), end),
-                        "unterminated byte constant",
-                        error_code!(E0763),
-                    )
+                    self.dcx()
+                        .struct_span_fatal(
+                            self.mk_sp(start + BytePos(1), end),
+                            "unterminated byte constant",
+                        )
+                        .with_code(error_code!(E0763))
+                        .emit()
                 }
                 self.cook_quoted(token::Byte, Mode::Byte, start, end, 2, 1) // b' '
             }
             rustc_lexer::LiteralKind::Str { terminated } => {
                 if !terminated {
-                    self.dcx().span_fatal_with_code(
-                        self.mk_sp(start, end),
-                        "unterminated double quote string",
-                        error_code!(E0765),
-                    )
+                    self.dcx()
+                        .struct_span_fatal(
+                            self.mk_sp(start, end),
+                            "unterminated double quote string",
+                        )
+                        .with_code(error_code!(E0765))
+                        .emit()
                 }
                 self.cook_quoted(token::Str, Mode::Str, start, end, 1, 1) // " "
             }
             rustc_lexer::LiteralKind::ByteStr { terminated } => {
                 if !terminated {
-                    self.dcx().span_fatal_with_code(
-                        self.mk_sp(start + BytePos(1), end),
-                        "unterminated double quote byte string",
-                        error_code!(E0766),
-                    )
+                    self.dcx()
+                        .struct_span_fatal(
+                            self.mk_sp(start + BytePos(1), end),
+                            "unterminated double quote byte string",
+                        )
+                        .with_code(error_code!(E0766))
+                        .emit()
                 }
                 self.cook_quoted(token::ByteStr, Mode::ByteStr, start, end, 2, 1) // b" "
             }
             rustc_lexer::LiteralKind::CStr { terminated } => {
                 if !terminated {
-                    self.dcx().span_fatal_with_code(
-                        self.mk_sp(start + BytePos(1), end),
-                        "unterminated C string",
-                        error_code!(E0767),
-                    )
+                    self.dcx()
+                        .struct_span_fatal(
+                            self.mk_sp(start + BytePos(1), end),
+                            "unterminated C string",
+                        )
+                        .with_code(error_code!(E0767))
+                        .emit()
                 }
                 self.cook_c_string(token::CStr, Mode::CStr, start, end, 2, 1) // c" "
             }
@@ -568,13 +561,16 @@ impl<'a> StringReader<'a> {
     }
 
     fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! {
-        self.struct_fatal_span_char(
-            start,
-            self.pos,
-            "found invalid character; only `#` is allowed in raw string delimitation",
-            bad_char,
-        )
-        .emit()
+        self.sess
+            .dcx
+            .struct_span_fatal(
+                self.mk_sp(start, self.pos),
+                format!(
+                    "found invalid character; only `#` is allowed in raw string delimitation: {}",
+                    escaped_char(bad_char)
+                ),
+            )
+            .emit()
     }
 
     fn report_unterminated_raw_string(
@@ -584,12 +580,9 @@ impl<'a> StringReader<'a> {
         possible_offset: Option<u32>,
         found_terminators: u32,
     ) -> ! {
-        let mut err = self.dcx().struct_span_fatal_with_code(
-            self.mk_sp(start, start),
-            "unterminated raw string",
-            error_code!(E0748),
-        );
-
+        let mut err =
+            self.dcx().struct_span_fatal(self.mk_sp(start, start), "unterminated raw string");
+        err.code(error_code!(E0748));
         err.span_label(self.mk_sp(start, start), "unterminated raw string");
 
         if n_hashes > 0 {
@@ -620,11 +613,8 @@ impl<'a> StringReader<'a> {
             None => "unterminated block comment",
         };
         let last_bpos = self.pos;
-        let mut err = self.dcx().struct_span_fatal_with_code(
-            self.mk_sp(start, last_bpos),
-            msg,
-            error_code!(E0758),
-        );
+        let mut err = self.dcx().struct_span_fatal(self.mk_sp(start, last_bpos), msg);
+        err.code(error_code!(E0758));
         let mut nested_block_comment_open_idxs = vec![];
         let mut last_nested_block_comment_idxs = None;
         let mut content_chars = self.str_from(start).char_indices().peekable();
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index b6cccd275ee..64b3928689a 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -277,9 +277,9 @@ impl<'a> TokenTreesReader<'a> {
             parser.bump();
         }
         if !diff_errs.is_empty() {
-            errs.iter_mut().for_each(|err| {
-                err.delay_as_bug();
-            });
+            for err in errs {
+                err.cancel();
+            }
             return diff_errs;
         }
         return errs;
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index 775082adbe8..fbc77f28780 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -264,14 +264,14 @@ pub(crate) fn emit_unescape_error(
         }
         EscapeError::UnskippedWhitespaceWarning => {
             let (c, char_span) = last_char();
-            dcx.emit_warning(UnescapeError::UnskippedWhitespace {
+            dcx.emit_warn(UnescapeError::UnskippedWhitespace {
                 span: err_span,
                 ch: escaped_char(c),
                 char_span,
             });
         }
         EscapeError::MultipleSkippedLinesWarning => {
-            dcx.emit_warning(UnescapeError::MultipleSkippedLinesWarning(err_span));
+            dcx.emit_warn(UnescapeError::MultipleSkippedLinesWarning(err_span));
         }
     }
 }
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index f06aeed8628..b93f08a21e3 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -154,7 +154,7 @@ fn try_file_to_source_file(
         let msg = format!("couldn't read {}: {}", path.display(), e);
         let mut diag = Diagnostic::new(Level::Fatal, msg);
         if let Some(sp) = spanopt {
-            diag.set_span(sp);
+            diag.span(sp);
         }
         diag
     })
@@ -245,9 +245,9 @@ pub fn parse_cfg_attr(
             crate::validate_attr::check_cfg_attr_bad_delim(parse_sess, dspan, delim);
             match parse_in(parse_sess, tokens.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
                 Ok(r) => return Some(r),
-                Err(mut e) => {
-                    e.help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
-                        .note(CFG_ATTR_NOTE_REF)
+                Err(e) => {
+                    e.with_help(format!("the valid syntax is `{CFG_ATTR_GRAMMAR_HELP}`"))
+                        .with_note(CFG_ATTR_NOTE_REF)
                         .emit();
                 }
             }
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index fb8ad05f25d..02dab95233a 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -55,11 +55,10 @@ impl<'a> Parser<'a> {
             } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
                 if attr_style != ast::AttrStyle::Outer {
                     let span = self.token.span;
-                    let mut err = self.dcx().struct_span_err_with_code(
-                        span,
-                        fluent::parse_inner_doc_comment_not_permitted,
-                        error_code!(E0753),
-                    );
+                    let mut err = self
+                        .dcx()
+                        .struct_span_err(span, fluent::parse_inner_doc_comment_not_permitted);
+                    err.code(error_code!(E0753));
                     if let Some(replacement_span) = self.annotate_following_item_if_applicable(
                         &mut err,
                         span,
@@ -174,7 +173,7 @@ impl<'a> Parser<'a> {
         ) {
             Ok(Some(item)) => {
                 // FIXME(#100717)
-                err.set_arg("item", item.kind.descr());
+                err.arg("item", item.kind.descr());
                 err.span_label(item.span, fluent::parse_label_does_not_annotate_this);
                 err.span_suggestion_verbose(
                     replacement_span,
@@ -200,23 +199,25 @@ impl<'a> Parser<'a> {
         if let InnerAttrPolicy::Forbidden(reason) = policy {
             let mut diag = match reason.as_ref().copied() {
                 Some(InnerAttrForbiddenReason::AfterOuterDocComment { prev_doc_comment_span }) => {
-                    let mut diag = self.dcx().struct_span_err(
-                        attr_sp,
-                        fluent::parse_inner_attr_not_permitted_after_outer_doc_comment,
-                    );
-                    diag.span_label(attr_sp, fluent::parse_label_attr)
-                        .span_label(prev_doc_comment_span, fluent::parse_label_prev_doc_comment);
-                    diag
+                    self.dcx()
+                        .struct_span_err(
+                            attr_sp,
+                            fluent::parse_inner_attr_not_permitted_after_outer_doc_comment,
+                        )
+                        .with_span_label(attr_sp, fluent::parse_label_attr)
+                        .with_span_label(
+                            prev_doc_comment_span,
+                            fluent::parse_label_prev_doc_comment,
+                        )
                 }
-                Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => {
-                    let mut diag = self.dcx().struct_span_err(
+                Some(InnerAttrForbiddenReason::AfterOuterAttribute { prev_outer_attr_sp }) => self
+                    .dcx()
+                    .struct_span_err(
                         attr_sp,
                         fluent::parse_inner_attr_not_permitted_after_outer_attr,
-                    );
-                    diag.span_label(attr_sp, fluent::parse_label_attr)
-                        .span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr);
-                    diag
-                }
+                    )
+                    .with_span_label(attr_sp, fluent::parse_label_attr)
+                    .with_span_label(prev_outer_attr_sp, fluent::parse_label_prev_attr),
                 Some(InnerAttrForbiddenReason::InCodeBlock) | None => {
                     self.dcx().struct_span_err(attr_sp, fluent::parse_inner_attr_not_permitted)
                 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 77bca2f138a..720a610fdf5 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -34,8 +34,8 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, FatalError,
-    PResult,
+    pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    ErrorGuaranteed, FatalError, PErr, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -846,7 +846,7 @@ impl<'a> Parser<'a> {
             ) =>
             {
                 let n_hashes: u8 = *n_hashes;
-                err.set_primary_message("too many `#` when terminating raw string");
+                err.primary_message("too many `#` when terminating raw string");
                 let str_span = self.prev_token.span;
                 let mut span = self.token.span;
                 let mut count = 0;
@@ -857,7 +857,7 @@ impl<'a> Parser<'a> {
                     self.bump();
                     count += 1;
                 }
-                err.set_span(span);
+                err.span(span);
                 err.span_suggestion(
                     span,
                     format!("remove the extra `#`{}", pluralize!(count)),
@@ -896,7 +896,7 @@ impl<'a> Parser<'a> {
             let struct_expr = snapshot.parse_expr_struct(None, path, false);
             let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
             return Some(match (struct_expr, block_tail) {
-                (Ok(expr), Err(mut err)) => {
+                (Ok(expr), Err(err)) => {
                     // We have encountered the following:
                     // fn foo() -> Foo {
                     //     field: value,
@@ -1049,9 +1049,9 @@ impl<'a> Parser<'a> {
         &mut self,
         segment: &PathSegment,
         end: &[&TokenKind],
-    ) -> bool {
+    ) -> Option<ErrorGuaranteed> {
         if !self.may_recover() {
-            return false;
+            return None;
         }
 
         // This function is intended to be invoked after parsing a path segment where there are two
@@ -1086,7 +1086,7 @@ impl<'a> Parser<'a> {
             parsed_angle_bracket_args,
         );
         if !parsed_angle_bracket_args {
-            return false;
+            return None;
         }
 
         // Keep the span at the start so we can highlight the sequence of `>` characters to be
@@ -1124,7 +1124,7 @@ impl<'a> Parser<'a> {
             number_of_gt, number_of_shr,
         );
         if number_of_gt < 1 && number_of_shr < 1 {
-            return false;
+            return None;
         }
 
         // Finally, double check that we have our end token as otherwise this is the
@@ -1139,10 +1139,9 @@ impl<'a> Parser<'a> {
             let span = lo.until(self.token.span);
 
             let num_extra_brackets = number_of_gt + number_of_shr * 2;
-            self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets });
-            return true;
+            return Some(self.dcx().emit_err(UnmatchedAngleBrackets { span, num_extra_brackets }));
         }
-        false
+        None
     }
 
     /// Check if a method call with an intended turbofish has been written without surrounding
@@ -1212,29 +1211,32 @@ impl<'a> Parser<'a> {
             match x {
                 Ok((_, _, false)) => {
                     if self.eat(&token::Gt) {
+                        // We made sense of it. Improve the error message.
                         e.span_suggestion_verbose(
                             binop.span.shrink_to_lo(),
                             fluent::parse_sugg_turbofish_syntax,
                             "::",
                             Applicability::MaybeIncorrect,
-                        )
-                        .emit();
+                        );
                         match self.parse_expr() {
                             Ok(_) => {
+                                // The subsequent expression is valid. Mark
+                                // `expr` as erroneous and emit `e` now, but
+                                // return `Ok` so parsing can continue.
+                                e.emit();
                                 *expr = self.mk_expr_err(expr.span.to(self.prev_token.span));
                                 return Ok(());
                             }
                             Err(err) => {
-                                *expr = self.mk_expr_err(expr.span);
                                 err.cancel();
                             }
                         }
                     }
                 }
+                Ok((_, _, true)) => {}
                 Err(err) => {
                     err.cancel();
                 }
-                _ => {}
             }
         }
         Err(e)
@@ -2041,17 +2043,12 @@ impl<'a> Parser<'a> {
         &mut self,
         delim: Delimiter,
         lo: Span,
-        result: PResult<'a, P<Expr>>,
+        err: PErr<'a>,
     ) -> P<Expr> {
-        match result {
-            Ok(x) => x,
-            Err(mut err) => {
-                err.emit();
-                // Recover from parse error, callers expect the closing delim to be consumed.
-                self.consume_block(delim, ConsumeClosingDelim::Yes);
-                self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err)
-            }
-        }
+        err.emit();
+        // Recover from parse error, callers expect the closing delim to be consumed.
+        self.consume_block(delim, ConsumeClosingDelim::Yes);
+        self.mk_expr(lo.to(self.prev_token.span), ExprKind::Err)
     }
 
     /// Eats tokens until we can be relatively sure we reached the end of the
@@ -2470,7 +2467,7 @@ impl<'a> Parser<'a> {
                     return Ok(true); // Continue
                 }
             }
-            Err(mut err) => {
+            Err(err) => {
                 args.push(arg);
                 // We will emit a more generic error later.
                 err.delay_as_bug();
@@ -2848,15 +2845,16 @@ impl<'a> Parser<'a> {
         let label = self.eat_label().expect("just checked if a label exists");
         self.bump(); // eat `:`
         let span = label.ident.span.to(self.prev_token.span);
-        let mut err = self.dcx().struct_span_err(span, "block label not supported here");
-        err.span_label(span, "not supported here");
-        err.tool_only_span_suggestion(
-            label.ident.span.until(self.token.span),
-            "remove this block label",
-            "",
-            Applicability::MachineApplicable,
-        );
-        err.emit();
+        self.dcx()
+            .struct_span_err(span, "block label not supported here")
+            .with_span_label(span, "not supported here")
+            .with_tool_only_span_suggestion(
+                label.ident.span.until(self.token.span),
+                "remove this block label",
+                "",
+                Applicability::MachineApplicable,
+            )
+            .emit();
         true
     }
 
@@ -2946,7 +2944,7 @@ impl<'a> Parser<'a> {
     }
 
     pub fn recover_diff_marker(&mut self) {
-        if let Err(mut err) = self.err_diff_marker() {
+        if let Err(err) = self.err_diff_marker() {
             err.emit();
             FatalError.raise();
         }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index bf6151b64d3..8ca02452342 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -127,7 +127,7 @@ impl<'a> Parser<'a> {
     fn parse_expr_catch_underscore(&mut self, restrictions: Restrictions) -> PResult<'a, P<Expr>> {
         match self.parse_expr_res(restrictions, None) {
             Ok(expr) => Ok(expr),
-            Err(mut err) => match self.token.ident() {
+            Err(err) => match self.token.ident() {
                 Some((Ident { name: kw::Underscore, .. }, false))
                     if self.may_recover() && self.look_ahead(1, |t| t == &token::Comma) =>
                 {
@@ -1023,7 +1023,7 @@ impl<'a> Parser<'a> {
     // we should break everything including floats into more basic proc-macro style
     // tokens in the lexer (probably preferable).
     // See also `TokenKind::break_two_token_op` which does similar splitting of `>>` into `>`.
-    fn break_up_float(&mut self, float: Symbol) -> DestructuredFloat {
+    fn break_up_float(&self, float: Symbol, span: Span) -> DestructuredFloat {
         #[derive(Debug)]
         enum FloatComponent {
             IdentLike(String),
@@ -1053,7 +1053,6 @@ impl<'a> Parser<'a> {
         // With proc macros the span can refer to anything, the source may be too short,
         // or too long, or non-ASCII. It only makes sense to break our span into components
         // if its underlying text is identical to our float literal.
-        let span = self.token.span;
         let can_take_span_apart =
             || self.span_to_snippet(span).as_deref() == Ok(float_str).as_deref();
 
@@ -1115,7 +1114,7 @@ impl<'a> Parser<'a> {
         float: Symbol,
         suffix: Option<Symbol>,
     ) -> P<Expr> {
-        match self.break_up_float(float) {
+        match self.break_up_float(float, self.token.span) {
             // 1e2
             DestructuredFloat::Single(sym, _sp) => {
                 self.parse_expr_tuple_field_access(lo, base, sym, suffix, None)
@@ -1143,40 +1142,105 @@ impl<'a> Parser<'a> {
         }
     }
 
-    fn parse_field_name_maybe_tuple(&mut self) -> PResult<'a, ThinVec<Ident>> {
-        let token::Literal(token::Lit { kind: token::Float, symbol, suffix }) = self.token.kind
-        else {
-            return Ok(thin_vec![self.parse_field_name()?]);
-        };
-        Ok(match self.break_up_float(symbol) {
-            // 1e2
-            DestructuredFloat::Single(sym, sp) => {
-                self.bump();
-                thin_vec![Ident::new(sym, sp)]
-            }
-            // 1.
-            DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {
-                assert!(suffix.is_none());
-                // Analogous to `Self::break_and_eat`
-                self.break_last_token = true;
-                // This might work, in cases like `1. 2`, and might not,
-                // in cases like `offset_of!(Ty, 1.)`. It depends on what comes
-                // after the float-like token, and therefore we have to make
-                // the other parts of the parser think that there is a dot literal.
-                self.token = Token::new(token::Ident(sym, false), sym_span);
-                self.bump_with((Token::new(token::Dot, dot_span), self.token_spacing));
-                thin_vec![Ident::new(sym, sym_span)]
-            }
-            // 1.2 | 1.2e3
-            DestructuredFloat::MiddleDot(symbol1, ident1_span, _dot_span, symbol2, ident2_span) => {
-                self.bump();
-                thin_vec![Ident::new(symbol1, ident1_span), Ident::new(symbol2, ident2_span)]
+    /// Parse the field access used in offset_of, matched by `$(e:expr)+`.
+    /// Currently returns a list of idents. However, it should be possible in
+    /// future to also do array indices, which might be arbitrary expressions.
+    fn parse_floating_field_access(&mut self) -> PResult<'a, P<[Ident]>> {
+        let mut fields = Vec::new();
+        let mut trailing_dot = None;
+
+        loop {
+            // This is expected to use a metavariable $(args:expr)+, but the builtin syntax
+            // could be called directly. Calling `parse_expr` allows this function to only
+            // consider `Expr`s.
+            let expr = self.parse_expr()?;
+            let mut current = &expr;
+            let start_idx = fields.len();
+            loop {
+                match current.kind {
+                    ExprKind::Field(ref left, right) => {
+                        // Field access is read right-to-left.
+                        fields.insert(start_idx, right);
+                        trailing_dot = None;
+                        current = left;
+                    }
+                    // Parse this both to give helpful error messages and to
+                    // verify it can be done with this parser setup.
+                    ExprKind::Index(ref left, ref _right, span) => {
+                        self.dcx().emit_err(errors::ArrayIndexInOffsetOf(span));
+                        current = left;
+                    }
+                    ExprKind::Lit(token::Lit {
+                        kind: token::Float | token::Integer,
+                        symbol,
+                        suffix,
+                    }) => {
+                        if let Some(suffix) = suffix {
+                            self.expect_no_tuple_index_suffix(current.span, suffix);
+                        }
+                        match self.break_up_float(symbol, current.span) {
+                            // 1e2
+                            DestructuredFloat::Single(sym, sp) => {
+                                trailing_dot = None;
+                                fields.insert(start_idx, Ident::new(sym, sp));
+                            }
+                            // 1.
+                            DestructuredFloat::TrailingDot(sym, sym_span, dot_span) => {
+                                assert!(suffix.is_none());
+                                trailing_dot = Some(dot_span);
+                                fields.insert(start_idx, Ident::new(sym, sym_span));
+                            }
+                            // 1.2 | 1.2e3
+                            DestructuredFloat::MiddleDot(
+                                symbol1,
+                                span1,
+                                _dot_span,
+                                symbol2,
+                                span2,
+                            ) => {
+                                trailing_dot = None;
+                                fields.insert(start_idx, Ident::new(symbol2, span2));
+                                fields.insert(start_idx, Ident::new(symbol1, span1));
+                            }
+                            DestructuredFloat::Error => {
+                                trailing_dot = None;
+                                fields.insert(start_idx, Ident::new(symbol, self.prev_token.span));
+                            }
+                        }
+                        break;
+                    }
+                    ExprKind::Path(None, Path { ref segments, .. }) => {
+                        match &segments[..] {
+                            [PathSegment { ident, args: None, .. }] => {
+                                trailing_dot = None;
+                                fields.insert(start_idx, *ident)
+                            }
+                            _ => {
+                                self.dcx().emit_err(errors::InvalidOffsetOf(current.span));
+                                break;
+                            }
+                        }
+                        break;
+                    }
+                    _ => {
+                        self.dcx().emit_err(errors::InvalidOffsetOf(current.span));
+                        break;
+                    }
+                }
             }
-            DestructuredFloat::Error => {
-                self.bump();
-                thin_vec![Ident::new(symbol, self.prev_token.span)]
+
+            if matches!(self.token.kind, token::CloseDelim(..) | token::Comma) {
+                break;
+            } else if trailing_dot.is_none() {
+                // This loop should only repeat if there is a trailing dot.
+                self.dcx().emit_err(errors::InvalidOffsetOf(self.token.span));
+                break;
             }
-        })
+        }
+        if let Some(dot) = trailing_dot {
+            self.dcx().emit_err(errors::InvalidOffsetOf(dot));
+        }
+        Ok(fields.into_iter().collect())
     }
 
     fn parse_expr_tuple_field_access(
@@ -1208,15 +1272,13 @@ impl<'a> Parser<'a> {
         };
         let open_paren = self.token.span;
 
-        let mut seq = self
+        let seq = self
             .parse_expr_paren_seq()
             .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)));
-        if let Some(expr) =
-            self.maybe_recover_struct_lit_bad_delims(lo, open_paren, &mut seq, snapshot)
-        {
-            return expr;
+        match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) {
+            Ok(expr) => expr,
+            Err(err) => self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err),
         }
-        self.recover_seq_parse_error(Delimiter::Parenthesis, lo, seq)
     }
 
     /// If we encounter a parser state that looks like the user has written a `struct` literal with
@@ -1226,14 +1288,11 @@ impl<'a> Parser<'a> {
         &mut self,
         lo: Span,
         open_paren: Span,
-        seq: &mut PResult<'a, P<Expr>>,
+        seq: PResult<'a, P<Expr>>,
         snapshot: Option<(SnapshotParser<'a>, ExprKind)>,
-    ) -> Option<P<Expr>> {
-        if !self.may_recover() {
-            return None;
-        }
-        match (seq.as_mut(), snapshot) {
-            (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
+    ) -> PResult<'a, P<Expr>> {
+        match (self.may_recover(), seq, snapshot) {
+            (true, Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
                 snapshot.bump(); // `(`
                 match snapshot.parse_struct_fields(path.clone(), false, Delimiter::Parenthesis) {
                     Ok((fields, ..))
@@ -1251,11 +1310,13 @@ impl<'a> Parser<'a> {
                         if !fields.is_empty() &&
                             // `token.kind` should not be compared here.
                             // This is because the `snapshot.token.kind` is treated as the same as
-                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even if they are different.
+                            // that of the open delim in `TokenTreesReader::parse_token_tree`, even
+                            // if they are different.
                             self.span_to_snippet(close_paren).is_ok_and(|snippet| snippet == ")")
                         {
-                            let mut replacement_err =
-                                self.dcx().create_err(errors::ParenthesesWithStructFields {
+                            err.cancel();
+                            self.dcx()
+                                .create_err(errors::ParenthesesWithStructFields {
                                     span,
                                     r#type: path,
                                     braces_for_struct: errors::BracesForStructLiteral {
@@ -1268,23 +1329,22 @@ impl<'a> Parser<'a> {
                                             .map(|field| field.span.until(field.expr.span))
                                             .collect(),
                                     },
-                                });
-                            replacement_err.emit();
-
-                            let old_err = mem::replace(err, replacement_err);
-                            old_err.cancel();
+                                })
+                                .emit();
                         } else {
                             err.emit();
                         }
-                        return Some(self.mk_expr_err(span));
+                        Ok(self.mk_expr_err(span))
+                    }
+                    Ok(_) => Err(err),
+                    Err(err2) => {
+                        err2.cancel();
+                        Err(err)
                     }
-                    Ok(_) => {}
-                    Err(err) => err.cancel(),
                 }
             }
-            _ => {}
+            (_, seq, _) => seq,
         }
-        None
     }
 
     /// Parse an indexing expression `expr[...]`.
@@ -1488,7 +1548,7 @@ impl<'a> Parser<'a> {
         ) {
             Ok(x) => x,
             Err(err) => {
-                return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, Err(err)));
+                return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, err));
             }
         };
         let kind = if es.len() == 1 && !trailing_comma {
@@ -1692,9 +1752,8 @@ impl<'a> Parser<'a> {
         mk_lit_char: impl FnOnce(Symbol, Span) -> L,
         err: impl FnOnce(&Self) -> DiagnosticBuilder<'a>,
     ) -> L {
-        if let Some(mut diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar)
-        {
-            diag.span_suggestion_verbose(
+        if let Some(diag) = self.dcx().steal_diagnostic(lifetime.span, StashKey::LifetimeIsChar) {
+            diag.with_span_suggestion_verbose(
                 lifetime.span.shrink_to_hi(),
                 "add `'` to close the char literal",
                 "'",
@@ -1703,7 +1762,7 @@ impl<'a> Parser<'a> {
             .emit();
         } else {
             err(self)
-                .span_suggestion_verbose(
+                .with_span_suggestion_verbose(
                     lifetime.span.shrink_to_hi(),
                     "add `'` to close the char literal",
                     "'",
@@ -1907,15 +1966,29 @@ impl<'a> Parser<'a> {
         let container = self.parse_ty()?;
         self.expect(&TokenKind::Comma)?;
 
-        let seq_sep = SeqSep { sep: Some(token::Dot), trailing_sep_allowed: false };
-        let (fields, _trailing, _recovered) = self.parse_seq_to_before_end(
-            &TokenKind::CloseDelim(Delimiter::Parenthesis),
-            seq_sep,
-            Parser::parse_field_name_maybe_tuple,
-        )?;
-        let fields = fields.into_iter().flatten().collect::<Vec<_>>();
+        let fields = self.parse_floating_field_access()?;
+        let trailing_comma = self.eat_noexpect(&TokenKind::Comma);
+
+        if let Err(mut e) =
+            self.expect_one_of(&[], &[TokenKind::CloseDelim(Delimiter::Parenthesis)])
+        {
+            if trailing_comma {
+                e.note("unexpected third argument to offset_of");
+            } else {
+                e.note("offset_of expects dot-separated field and variant names");
+            }
+            e.emit();
+        }
+
+        // Eat tokens until the macro call ends.
+        if self.may_recover() {
+            while !matches!(self.token.kind, token::CloseDelim(..) | token::Eof) {
+                self.bump();
+            }
+        }
+
         let span = lo.to(self.token.span);
-        Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields.into())))
+        Ok(self.mk_expr(span, ExprKind::OffsetOf(container, fields)))
     }
 
     /// Returns a string literal if the next token is a string literal.
@@ -2077,7 +2150,7 @@ impl<'a> Parser<'a> {
         if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) {
             // #59553: warn instead of reject out of hand to allow the fix to percolate
             // through the ecosystem when people fix their macros
-            self.dcx().emit_warning(errors::InvalidLiteralSuffixOnTupleIndex {
+            self.dcx().emit_warn(errors::InvalidLiteralSuffixOnTupleIndex {
                 span,
                 suffix,
                 exception: Some(()),
@@ -2411,7 +2484,7 @@ impl<'a> Parser<'a> {
                 }
                 ExprKind::Block(_, None) => {
                     this.dcx().emit_err(errors::IfExpressionMissingCondition {
-                        if_span: lo.shrink_to_hi(),
+                        if_span: lo.with_neighbor(cond.span).shrink_to_hi(),
                         block_span: self.sess.source_map().start_point(cond_span),
                     });
                     std::mem::replace(&mut cond, this.mk_expr_err(cond_span.shrink_to_hi()))
@@ -2825,7 +2898,7 @@ impl<'a> Parser<'a> {
         while self.token != token::CloseDelim(Delimiter::Brace) {
             match self.parse_arm() {
                 Ok(arm) => arms.push(arm),
-                Err(mut e) => {
+                Err(e) => {
                     // Recover by skipping to the end of the block.
                     e.emit();
                     self.recover_stmt();
@@ -3359,7 +3432,7 @@ impl<'a> Parser<'a> {
                 }
                 match self.parse_expr() {
                     Ok(e) => base = ast::StructRest::Base(e),
-                    Err(mut e) if recover => {
+                    Err(e) if recover => {
                         e.emit();
                         self.recover_stmt();
                     }
@@ -3657,7 +3730,7 @@ impl<'a> Parser<'a> {
     }
 
     pub(crate) fn mk_expr(&self, span: Span, kind: ExprKind) -> P<Expr> {
-        P(Expr { kind, span, attrs: AttrVec::new(), id: DUMMY_NODE_ID, tokens: None })
+        self.mk_expr_with_attrs(span, kind, AttrVec::new())
     }
 
     pub(super) fn mk_expr_err(&self, span: Span) -> P<Expr> {
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 7e243c1c32a..48cf04f7790 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -77,7 +77,6 @@ impl<'a> Parser<'a> {
                                     Applicability::MachineApplicable,
                                 );
                             }
-                            err.emit();
                             return Err(err);
                         }
                     }
@@ -141,17 +140,18 @@ impl<'a> Parser<'a> {
         // Parse optional const generics default value.
         let default = if self.eat(&token::Eq) { Some(self.parse_const_arg()?) } else { None };
 
-        let mut err = self.dcx().struct_span_err(
-            mistyped_const_ident.span,
-            format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()),
-        );
-        err.span_suggestion_verbose(
-            mistyped_const_ident.span,
-            "use the `const` keyword",
-            kw::Const.as_str(),
-            Applicability::MachineApplicable,
-        );
-        err.emit();
+        self.dcx()
+            .struct_span_err(
+                mistyped_const_ident.span,
+                format!("`const` keyword was mistyped as `{}`", mistyped_const_ident.as_str()),
+            )
+            .with_span_suggestion_verbose(
+                mistyped_const_ident.span,
+                "use the `const` keyword",
+                kw::Const.as_str(),
+                Applicability::MachineApplicable,
+            )
+            .emit();
 
         Ok(GenericParam {
             ident,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 0ac0b678aba..bcff820da79 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -10,7 +10,7 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_ast::util::case::Case;
 use rustc_ast::{self as ast};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
+use rustc_errors::{struct_span_code_err, Applicability, PResult, StashKey};
 use rustc_span::edit_distance::edit_distance;
 use rustc_span::edition::Edition;
 use rustc_span::source_map;
@@ -739,11 +739,14 @@ impl<'a> Parser<'a> {
                     break;
                 }
                 Ok(Some(item)) => items.extend(item),
-                Err(mut err) => {
+                Err(err) => {
                     self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
-                    err.span_label(open_brace_span, "while parsing this item list starting here")
-                        .span_label(self.prev_token.span, "the item list ends here")
-                        .emit();
+                    err.with_span_label(
+                        open_brace_span,
+                        "while parsing this item list starting here",
+                    )
+                    .with_span_label(self.prev_token.span, "the item list ends here")
+                    .emit();
                     break;
                 }
             }
@@ -756,14 +759,14 @@ impl<'a> Parser<'a> {
         if let token::DocComment(..) = self.token.kind {
             if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) {
                 // FIXME: merge with `DocCommentDoesNotDocumentAnything` (E0585)
-                struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     self.token.span,
                     E0584,
                     "found a documentation comment that doesn't document anything",
                 )
-                .span_label(self.token.span, "this doc comment doesn't document anything")
-                .help(
+                .with_span_label(self.token.span, "this doc comment doesn't document anything")
+                .with_help(
                     "doc comments must come before what they document, if a comment was \
                     intended use `//`",
                 )
@@ -1106,8 +1109,7 @@ impl<'a> Parser<'a> {
             && self.token.is_keyword(kw::Unsafe)
             && self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Brace))
         {
-            let mut err = self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err();
-            err.emit();
+            self.expect(&token::OpenDelim(Delimiter::Brace)).unwrap_err().emit();
             unsafety = Unsafe::Yes(self.token.span);
             self.eat_keyword(kw::Unsafe);
         }
@@ -1198,7 +1200,7 @@ impl<'a> Parser<'a> {
         defaultness: Defaultness,
     ) -> PResult<'a, ItemInfo> {
         let impl_span = self.token.span;
-        let mut err = self.expected_ident_found_err();
+        let err = self.expected_ident_found_err();
 
         // Only try to recover if this is implementing a trait for a type
         let mut impl_info = match self.parse_item_impl(attrs, defaultness) {
@@ -1216,7 +1218,7 @@ impl<'a> Parser<'a> {
 
                 let before_trait = trai.path.span.shrink_to_lo();
                 let const_up_to_impl = const_span.with_hi(impl_span.lo());
-                err.multipart_suggestion(
+                err.with_multipart_suggestion(
                     "you might have meant to write a const trait impl",
                     vec![(const_up_to_impl, "".to_owned()), (before_trait, "const ".to_owned())],
                     Applicability::MaybeIncorrect,
@@ -1454,8 +1456,8 @@ impl<'a> Parser<'a> {
                 let ident = this.parse_field_ident("enum", vlo)?;
 
                 if this.token == token::Not {
-                    if let Err(mut err) = this.unexpected::<()>() {
-                        err.note(fluent::parse_macro_expands_to_enum_variant).emit();
+                    if let Err(err) = this.unexpected::<()>() {
+                        err.with_note(fluent::parse_macro_expands_to_enum_variant).emit();
                     }
 
                     this.bump();
@@ -1791,37 +1793,36 @@ impl<'a> Parser<'a> {
             }
             _ => {
                 let sp = self.prev_token.span.shrink_to_hi();
-                let mut err = self.dcx().struct_span_err(
-                    sp,
-                    format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token)),
-                );
+                let msg =
+                    format!("expected `,`, or `}}`, found {}", super::token_descr(&self.token));
 
                 // Try to recover extra trailing angle brackets
-                let mut recovered = false;
                 if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind {
                     if let Some(last_segment) = segments.last() {
-                        recovered = self.check_trailing_angle_brackets(
+                        let guar = self.check_trailing_angle_brackets(
                             last_segment,
                             &[&token::Comma, &token::CloseDelim(Delimiter::Brace)],
                         );
-                        if recovered {
+                        if let Some(_guar) = guar {
                             // Handle a case like `Vec<u8>>,` where we can continue parsing fields
                             // after the comma
                             self.eat(&token::Comma);
-                            // `check_trailing_angle_brackets` already emitted a nicer error
-                            // NOTE(eddyb) this was `.cancel()`, but `err`
-                            // gets returned, so we can't fully defuse it.
-                            err.delay_as_bug();
+
+                            // `check_trailing_angle_brackets` already emitted a nicer error, as
+                            // proven by the presence of `_guar`. We can continue parsing.
+                            return Ok(a_var);
                         }
                     }
                 }
 
+                let mut err = self.dcx().struct_span_err(sp, msg);
+
                 if self.token.is_ident()
                     || (self.token.kind == TokenKind::Pound
                         && (self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Bracket))))
                 {
-                    // This is likely another field, TokenKind::Pound is used for `#[..]` attribute for next field,
-                    // emit the diagnostic and keep going
+                    // This is likely another field, TokenKind::Pound is used for `#[..]`
+                    // attribute for next field. Emit the diagnostic and continue parsing.
                     err.span_suggestion(
                         sp,
                         "try adding a comma",
@@ -1829,14 +1830,6 @@ impl<'a> Parser<'a> {
                         Applicability::MachineApplicable,
                     );
                     err.emit();
-                    recovered = true;
-                }
-
-                if recovered {
-                    // Make sure an error was emitted (either by recovering an angle bracket,
-                    // or by finding an identifier as the next token), since we're
-                    // going to continue parsing
-                    assert!(self.dcx().has_errors().is_some());
                 } else {
                     return Err(err);
                 }
@@ -1846,7 +1839,7 @@ impl<'a> Parser<'a> {
     }
 
     fn expect_field_ty_separator(&mut self) -> PResult<'a, ()> {
-        if let Err(mut err) = self.expect(&token::Colon) {
+        if let Err(err) = self.expect(&token::Colon) {
             let sm = self.sess.source_map();
             let eq_typo = self.token.kind == token::Eq && self.look_ahead(1, |t| t.is_path_start());
             let semi_typo = self.token.kind == token::Semi
@@ -1862,7 +1855,7 @@ impl<'a> Parser<'a> {
             if eq_typo || semi_typo {
                 self.bump();
                 // Gracefully handle small typos.
-                err.span_suggestion_short(
+                err.with_span_suggestion_short(
                     self.prev_token.span,
                     "field names and their types are separated with `:`",
                     ":",
@@ -1938,15 +1931,14 @@ impl<'a> Parser<'a> {
                     Case::Insensitive,
                 ) {
                     Ok(_) => {
-                        let mut err = self.dcx().struct_span_err(
+                        self.dcx().struct_span_err(
                             lo.to(self.prev_token.span),
                             format!("functions are not allowed in {adt_ty} definitions"),
-                        );
-                        err.help(
+                        )
+                        .with_help(
                             "unlike in C++, Java, and C#, functions are declared in `impl` blocks",
-                        );
-                        err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
-                        err
+                        )
+                        .with_help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information")
                     }
                     Err(err) => {
                         err.cancel();
@@ -1956,14 +1948,15 @@ impl<'a> Parser<'a> {
                 }
             } else if self.eat_keyword(kw::Struct) {
                 match self.parse_item_struct() {
-                    Ok((ident, _)) => {
-                        let mut err = self.dcx().struct_span_err(
+                    Ok((ident, _)) => self
+                        .dcx()
+                        .struct_span_err(
                             lo.with_hi(ident.span.hi()),
                             format!("structs are not allowed in {adt_ty} definitions"),
-                        );
-                        err.help("consider creating a new `struct` definition instead of nesting");
-                        err
-                    }
+                        )
+                        .with_help(
+                            "consider creating a new `struct` definition instead of nesting",
+                        ),
                     Err(err) => {
                         err.cancel();
                         self.restore_snapshot(snapshot);
@@ -2118,7 +2111,7 @@ impl<'a> Parser<'a> {
                 Applicability::MaybeIncorrect,
             );
             err.span_suggestion(
-                span.shrink_to_hi(),
+                span.with_neighbor(self.token.span).shrink_to_hi(),
                 "add a semicolon",
                 ';',
                 Applicability::MaybeIncorrect,
@@ -2598,7 +2591,7 @@ impl<'a> Parser<'a> {
         let (mut params, _) = self.parse_paren_comma_seq(|p| {
             p.recover_diff_marker();
             let snapshot = p.create_snapshot_for_diagnostic();
-            let param = p.parse_param_general(req_name, first_param).or_else(|mut e| {
+            let param = p.parse_param_general(req_name, first_param).or_else(|e| {
                 e.emit();
                 let lo = p.prev_token.span;
                 p.restore_snapshot(snapshot);
@@ -2632,7 +2625,7 @@ impl<'a> Parser<'a> {
 
             let is_name_required = match this.token.kind {
                 token::DotDotDot => false,
-                _ => req_name(this.token.span.edition()),
+                _ => req_name(this.token.span.with_neighbor(this.prev_token.span).edition()),
             };
             let (pat, ty) = if is_name_required || this.is_named_param() {
                 debug!("parse_param_general parse_pat (is_name_required:{})", is_name_required);
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 3932d32a6e1..ff2fb6271a8 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -499,7 +499,7 @@ impl<'a> Parser<'a> {
         let (ident, is_raw) = self.ident_or_err(recover)?;
 
         if !is_raw && ident.is_reserved() {
-            let mut err = self.expected_ident_found_err();
+            let err = self.expected_ident_found_err();
             if recover {
                 err.emit();
             } else {
@@ -847,7 +847,7 @@ impl<'a> Parser<'a> {
                                     pprust::token_to_string(&self.prev_token)
                                 );
                                 expect_err
-                                    .span_suggestion_verbose(
+                                    .with_span_suggestion_verbose(
                                         self.prev_token.span.shrink_to_hi().until(self.token.span),
                                         msg,
                                         " @ ",
@@ -863,7 +863,7 @@ impl<'a> Parser<'a> {
                                     // Parsed successfully, therefore most probably the code only
                                     // misses a separator.
                                     expect_err
-                                        .span_suggestion_short(
+                                        .with_span_suggestion_short(
                                             sp,
                                             format!("missing `{token_str}`"),
                                             token_str,
@@ -925,9 +925,8 @@ impl<'a> Parser<'a> {
                 });
         }
 
-        expect_err.set_primary_message(
-            "closure bodies that contain statements must be surrounded by braces",
-        );
+        expect_err
+            .primary_message("closure bodies that contain statements must be surrounded by braces");
 
         let preceding_pipe_span = closure_spans.closing_pipe;
         let following_token_span = self.token.span;
@@ -951,7 +950,7 @@ impl<'a> Parser<'a> {
         );
         expect_err.span_note(second_note, "the closure body may be incorrectly delimited");
 
-        expect_err.set_span(vec![preceding_pipe_span, following_token_span]);
+        expect_err.span(vec![preceding_pipe_span, following_token_span]);
 
         let opening_suggestion_str = " {".to_string();
         let closing_suggestion_str = "}".to_string();
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index afbc2537578..7918e03750c 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -6,7 +6,8 @@ use crate::errors::{
     InclusiveRangeExtraEquals, InclusiveRangeMatchArrow, InclusiveRangeNoEnd, InvalidMutInPattern,
     PatternOnWrongSideOfAt, RefMutOrderIncorrect, RemoveLet, RepeatedMutInPattern,
     SwitchRefBoxOrder, TopLevelOrPatternNotAllowed, TopLevelOrPatternNotAllowedSugg,
-    TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedVertVertBeforeFunctionParam,
+    TrailingVertNotAllowed, UnexpectedLifetimeInPattern, UnexpectedParenInRangePat,
+    UnexpectedParenInRangePatSugg, UnexpectedVertVertBeforeFunctionParam,
     UnexpectedVertVertInPattern,
 };
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
@@ -143,7 +144,7 @@ impl<'a> Parser<'a> {
         // Parse the first pattern (`p_0`).
         let mut first_pat = match self.parse_pat_no_top_alt(expected, syntax_loc) {
             Ok(pat) => pat,
-            Err(mut err)
+            Err(err)
                 if self.token.is_reserved_ident()
                     && !self.token.is_keyword(kw::In)
                     && !self.token.is_keyword(kw::If) =>
@@ -241,7 +242,7 @@ impl<'a> Parser<'a> {
                 Some(TopLevelOrPatternNotAllowedSugg::WrapInParens { span, pat })
             };
 
-            let mut err = self.dcx().create_err(match syntax_loc {
+            let err = self.dcx().create_err(match syntax_loc {
                 PatternLocation::LetBinding => {
                     TopLevelOrPatternNotAllowed::LetBinding { span, sub }
                 }
@@ -251,8 +252,9 @@ impl<'a> Parser<'a> {
             });
             if trailing_vert {
                 err.delay_as_bug();
+            } else {
+                err.emit();
             }
-            err.emit();
         }
 
         Ok((pat, colon))
@@ -459,9 +461,10 @@ impl<'a> Parser<'a> {
                         super::token_descr(&self_.token)
                     );
 
-                    let mut err = self_.dcx().struct_span_err(self_.token.span, msg);
-                    err.span_label(self_.token.span, format!("expected {expected}"));
-                    err
+                    self_
+                        .dcx()
+                        .struct_span_err(self_.token.span, msg)
+                        .with_span_label(self_.token.span, format!("expected {expected}"))
                 });
             PatKind::Lit(self.mk_expr(lo, ExprKind::Lit(lit)))
         } else {
@@ -579,6 +582,8 @@ impl<'a> Parser<'a> {
 
     /// Parse a tuple or parenthesis pattern.
     fn parse_pat_tuple_or_parens(&mut self) -> PResult<'a, PatKind> {
+        let open_paren = self.token.span;
+
         let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| {
             p.parse_pat_allow_top_alt(
                 None,
@@ -591,7 +596,29 @@ impl<'a> Parser<'a> {
         // Here, `(pat,)` is a tuple pattern.
         // For backward compatibility, `(..)` is a tuple pattern as well.
         Ok(if fields.len() == 1 && !(trailing_comma || fields[0].is_rest()) {
-            PatKind::Paren(fields.into_iter().next().unwrap())
+            let pat = fields.into_iter().next().unwrap();
+            let close_paren = self.prev_token.span;
+
+            match &pat.kind {
+                // recover ranges with parentheses around the `(start)..`
+                PatKind::Lit(begin)
+                    if self.may_recover()
+                        && let Some(form) = self.parse_range_end() =>
+                {
+                    self.dcx().emit_err(UnexpectedParenInRangePat {
+                        span: vec![open_paren, close_paren],
+                        sugg: UnexpectedParenInRangePatSugg {
+                            start_span: open_paren,
+                            end_span: close_paren,
+                        },
+                    });
+
+                    self.parse_pat_range_begin_with(begin.clone(), form)?
+                }
+
+                // (pat) with optional parentheses
+                _ => PatKind::Paren(pat),
+            }
         } else {
             PatKind::Tuple(fields)
         })
@@ -794,11 +821,21 @@ impl<'a> Parser<'a> {
                 || t.can_begin_literal_maybe_minus() // e.g. `42`.
                 || t.is_whole_expr()
                 || t.is_lifetime() // recover `'a` instead of `'a'`
+                || (self.may_recover() // recover leading `(`
+                    && t.kind == token::OpenDelim(Delimiter::Parenthesis)
+                    && self.look_ahead(dist + 1, |t| t.kind != token::OpenDelim(Delimiter::Parenthesis))
+                    && self.is_pat_range_end_start(dist + 1))
             })
     }
 
+    /// Parse a range pattern end bound
     fn parse_pat_range_end(&mut self) -> PResult<'a, P<Expr>> {
-        if self.check_inline_const(0) {
+        // recover leading `(`
+        let open_paren = (self.may_recover()
+            && self.eat_noexpect(&token::OpenDelim(Delimiter::Parenthesis)))
+        .then_some(self.prev_token.span);
+
+        let bound = if self.check_inline_const(0) {
             self.parse_const_block(self.token.span, true)
         } else if self.check_path() {
             let lo = self.token.span;
@@ -814,7 +851,22 @@ impl<'a> Parser<'a> {
             Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path)))
         } else {
             self.parse_literal_maybe_minus()
+        }?;
+
+        // recover trailing `)`
+        if let Some(open_paren) = open_paren {
+            self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
+
+            self.dcx().emit_err(UnexpectedParenInRangePat {
+                span: vec![open_paren, self.prev_token.span],
+                sugg: UnexpectedParenInRangePatSugg {
+                    start_span: open_paren,
+                    end_span: self.prev_token.span,
+                },
+            });
         }
+
+        Ok(bound)
     }
 
     /// Is this the start of a pattern beginning with a path?
@@ -978,7 +1030,7 @@ impl<'a> Parser<'a> {
             let attrs = match self.parse_outer_attributes() {
                 Ok(attrs) => attrs,
                 Err(err) => {
-                    if let Some(mut delayed) = delayed_err {
+                    if let Some(delayed) = delayed_err {
                         delayed.emit();
                     }
                     return Err(err);
@@ -990,7 +1042,7 @@ impl<'a> Parser<'a> {
             if !ate_comma {
                 let mut err =
                     self.dcx().create_err(ExpectedCommaAfterPatternField { span: self.token.span });
-                if let Some(mut delayed) = delayed_err {
+                if let Some(delayed) = delayed_err {
                     delayed.emit();
                 }
                 self.recover_misplaced_pattern_modifiers(&fields, &mut err);
@@ -1063,14 +1115,14 @@ impl<'a> Parser<'a> {
                     // This way we avoid "pattern missing fields" errors afterwards.
                     // We delay this error until the end in order to have a span for a
                     // suggested fix.
-                    if let Some(mut delayed_err) = delayed_err {
+                    if let Some(delayed_err) = delayed_err {
                         delayed_err.emit();
                         return Err(err);
                     } else {
                         delayed_err = Some(err);
                     }
                 } else {
-                    if let Some(mut err) = delayed_err {
+                    if let Some(err) = delayed_err {
                         err.emit();
                     }
                     return Err(err);
@@ -1082,7 +1134,7 @@ impl<'a> Parser<'a> {
                     let field = match this.parse_pat_field(lo, attrs) {
                         Ok(field) => Ok(field),
                         Err(err) => {
-                            if let Some(mut delayed_err) = delayed_err.take() {
+                            if let Some(delayed_err) = delayed_err.take() {
                                 delayed_err.emit();
                             }
                             return Err(err);
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 4253c0ae421..e7cad74b4dd 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -128,7 +128,7 @@ impl<'a> Parser<'a> {
                 self.prev_token.span,
                 "found single colon before projection in qualified path",
             )
-            .span_suggestion(
+            .with_span_suggestion(
                 self.prev_token.span,
                 "use double colon",
                 "::",
@@ -493,7 +493,7 @@ impl<'a> Parser<'a> {
                 self.angle_bracket_nesting -= 1;
                 Ok(args)
             }
-            Err(mut e) if self.angle_bracket_nesting > 10 => {
+            Err(e) if self.angle_bracket_nesting > 10 => {
                 self.angle_bracket_nesting -= 1;
                 // When encountering severely malformed code where there are several levels of
                 // nested unclosed angle args (`f::<f::<f::<f::<...`), we avoid severe O(n^2)
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 831edcd88c1..1a8bbf0a157 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -32,7 +32,7 @@ impl<'a> Parser<'a> {
     /// e.g., a `StmtKind::Semi` parses to a `StmtKind::Expr`, leaving the trailing `;` unconsumed.
     // Public for rustfmt usage.
     pub fn parse_stmt(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Stmt>> {
-        Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|mut e| {
+        Ok(self.parse_stmt_without_recovery(false, force_collect).unwrap_or_else(|e| {
             e.emit();
             self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
             None
@@ -663,7 +663,7 @@ impl<'a> Parser<'a> {
                     match expect_result {
                         // Recover from parser, skip type error to avoid extra errors.
                         Ok(true) => true,
-                        Err(mut e) => {
+                        Err(e) => {
                             if self.recover_colon_as_semi() {
                                 // recover_colon_as_semi has already emitted a nicer error.
                                 e.delay_as_bug();
@@ -716,7 +716,7 @@ impl<'a> Parser<'a> {
                                 _ => {}
                             }
 
-                            if let Err(mut e) =
+                            if let Err(e) =
                                 self.check_mistyped_turbofish_with_multiple_type_params(e, expr)
                             {
                                 if recover.no() {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 4be2c662d03..a4fb92c67ac 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -300,7 +300,7 @@ impl<'a> Parser<'a> {
                     let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
                     let kind =
                         self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?;
-                    let mut err = self.dcx().create_err(errors::TransposeDynOrImpl {
+                    let err = self.dcx().create_err(errors::TransposeDynOrImpl {
                         span: kw.span,
                         kw: kw.name.as_str(),
                         sugg: errors::TransposeDynOrImplSugg {
@@ -487,7 +487,7 @@ impl<'a> Parser<'a> {
     fn parse_array_or_slice_ty(&mut self) -> PResult<'a, TyKind> {
         let elt_ty = match self.parse_ty() {
             Ok(ty) => ty,
-            Err(mut err)
+            Err(err)
                 if self.look_ahead(1, |t| t.kind == token::CloseDelim(Delimiter::Bracket))
                     | self.look_ahead(1, |t| t.kind == token::Semi) =>
             {
@@ -1109,20 +1109,19 @@ impl<'a> Parser<'a> {
         lifetime_defs.append(&mut generic_params);
 
         let generic_args_span = generic_args.span();
-        let mut err = self
-            .dcx()
-            .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters");
         let snippet = format!(
             "for<{}> ",
             lifetimes.iter().map(|lt| lt.ident.as_str()).intersperse(", ").collect::<String>(),
         );
         let before_fn_path = fn_path.span.shrink_to_lo();
-        err.multipart_suggestion(
-            "consider using a higher-ranked trait bound instead",
-            vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)],
-            Applicability::MaybeIncorrect,
-        )
-        .emit();
+        self.dcx()
+            .struct_span_err(generic_args_span, "`Fn` traits cannot take lifetime parameters")
+            .with_multipart_suggestion(
+                "consider using a higher-ranked trait bound instead",
+                vec![(generic_args_span, "".to_owned()), (before_fn_path, snippet)],
+                Applicability::MaybeIncorrect,
+            )
+            .emit();
         Ok(())
     }
 
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 4efb1be03be..2fafbd6d97b 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -29,7 +29,7 @@ pub fn check_attr(sess: &ParseSess, attr: &Attribute) {
         _ if let AttrArgs::Eq(..) = attr.get_normal_item().args => {
             // All key-value attributes are restricted to meta-item syntax.
             parse_meta(sess, attr)
-                .map_err(|mut err| {
+                .map_err(|err| {
                     err.emit();
                 })
                 .ok();
@@ -139,7 +139,7 @@ pub fn check_builtin_attribute(
 ) {
     match parse_meta(sess, attr) {
         Ok(meta) => check_builtin_meta_item(sess, &meta, attr.style, name, template),
-        Err(mut err) => {
+        Err(err) => {
             err.emit();
         }
     }
@@ -208,7 +208,7 @@ fn emit_malformed_attribute(
     } else {
         sess.dcx
             .struct_span_err(span, error_msg)
-            .span_suggestions(
+            .with_span_suggestions(
                 span,
                 if suggestions.len() == 1 {
                     "must be of the form"
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index e886db3da29..1e1d45d6f70 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -289,10 +289,10 @@ impl<'a> Iterator for Parser<'a> {
                             }
                         } else {
                             if let Some(&(_, maybe)) = self.cur.peek() {
-                                if maybe == '?' {
-                                    self.suggest_format();
-                                } else {
-                                    self.suggest_positional_arg_instead_of_captured_arg(arg);
+                                match maybe {
+                                    '?' => self.suggest_format_debug(),
+                                    '<' | '^' | '>' => self.suggest_format_align(maybe),
+                                    _ => self.suggest_positional_arg_instead_of_captured_arg(arg),
                                 }
                             }
                         }
@@ -868,10 +868,9 @@ impl<'a> Parser<'a> {
         found.then_some(cur)
     }
 
-    fn suggest_format(&mut self) {
+    fn suggest_format_debug(&mut self) {
         if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) {
             let word = self.word();
-            let _end = self.current_pos();
             let pos = self.to_span_index(pos);
             self.errors.insert(
                 0,
@@ -887,6 +886,23 @@ impl<'a> Parser<'a> {
         }
     }
 
+    fn suggest_format_align(&mut self, alignment: char) {
+        if let Some(pos) = self.consume_pos(alignment) {
+            let pos = self.to_span_index(pos);
+            self.errors.insert(
+                0,
+                ParseError {
+                    description: "expected format parameter to occur after `:`".to_owned(),
+                    note: None,
+                    label: format!("expected `{}` to occur after `:`", alignment).to_owned(),
+                    span: pos.to(pos),
+                    secondary_label: None,
+                    suggestion: Suggestion::None,
+                },
+            );
+        }
+    }
+
     fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) {
         if let Some(end) = self.consume_pos('.') {
             let byte_pos = self.to_span_index(end);
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index be50aad1303..d41cc724408 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -575,6 +575,11 @@ passes_outside_loop =
 
 passes_outside_loop_suggestion = consider labeling this block to be able to break within it
 
+passes_panic_unwind_without_std =
+    unwinding panics are not supported without std
+    .note = since the core library is usually precompiled with panic="unwind", rebuilding your crate with panic="abort" may not be enough to fix the problem
+    .help = using nightly cargo, use -Zbuild-std with panic="abort" to avoid unwinding
+
 passes_params_not_allowed =
     referencing function parameters is not allowed in naked functions
     .help = follow the calling convention in asm block to use parameters
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index edce99db705..e4bbc9eeaf7 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -2341,17 +2341,17 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 match terr {
                     TypeError::ArgumentMutability(idx) | TypeError::ArgumentSorts(_, idx) => {
                         if let Some(ty) = hir_sig.decl.inputs.get(idx) {
-                            diag.set_span(ty.span);
+                            diag.span(ty.span);
                             cause.span = ty.span;
                         } else if idx == hir_sig.decl.inputs.len() {
                             let span = hir_sig.decl.output.span();
-                            diag.set_span(span);
+                            diag.span(span);
                             cause.span = span;
                         }
                     }
                     TypeError::ArgCount => {
                         if let Some(ty) = hir_sig.decl.inputs.get(expected_sig.inputs().len()) {
-                            diag.set_span(ty.span);
+                            diag.span(ty.span);
                             cause.span = ty.span;
                         }
                     }
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 3e5fb1a6b47..3e3f2771f5f 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> {
             // corresponding feature gate. This encourages nightly users to use feature gates when
             // possible.
             None if tcx.sess.opts.unstable_opts.unleash_the_miri_inside_of_you => {
-                tcx.dcx().emit_warning(SkippingConstChecks { span });
+                tcx.dcx().emit_warn(SkippingConstChecks { span });
                 return;
             }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 22aac1e775e..ac2ca23ad41 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -15,8 +15,8 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::lint::builtin::{DEAD_CODE, UNUSED_TUPLE_STRUCT_FIELDS};
-use rustc_session::lint::{self, Lint, LintId};
+use rustc_session::lint;
+use rustc_session::lint::builtin::DEAD_CODE;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_target::abi::FieldIdx;
 use std::mem;
@@ -766,6 +766,12 @@ enum ShouldWarnAboutField {
     No,
 }
 
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+enum ReportOn {
+    TupleField,
+    NamedField,
+}
+
 impl<'tcx> DeadVisitor<'tcx> {
     fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> ShouldWarnAboutField {
         if self.live_symbols.contains(&field.did.expect_local()) {
@@ -787,9 +793,9 @@ impl<'tcx> DeadVisitor<'tcx> {
         ShouldWarnAboutField::Yes
     }
 
-    fn def_lint_level(&self, lint: &'static Lint, id: LocalDefId) -> lint::Level {
+    fn def_lint_level(&self, id: LocalDefId) -> lint::Level {
         let hir_id = self.tcx.local_def_id_to_hir_id(id);
-        self.tcx.lint_level_at_node(lint, hir_id).0
+        self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0
     }
 
     // # Panics
@@ -803,7 +809,7 @@ impl<'tcx> DeadVisitor<'tcx> {
         dead_codes: &[&DeadItem],
         participle: &str,
         parent_item: Option<LocalDefId>,
-        lint: &'static Lint,
+        report_on: ReportOn,
     ) {
         let Some(&first_item) = dead_codes.first() else {
             return;
@@ -864,8 +870,8 @@ impl<'tcx> DeadVisitor<'tcx> {
                 None
             };
 
-        let diag = if LintId::of(lint) == LintId::of(UNUSED_TUPLE_STRUCT_FIELDS) {
-            MultipleDeadCodes::UnusedTupleStructFields {
+        let diag = match report_on {
+            ReportOn::TupleField => MultipleDeadCodes::UnusedTupleStructFields {
                 multiple,
                 num,
                 descr,
@@ -874,9 +880,9 @@ impl<'tcx> DeadVisitor<'tcx> {
                 change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() },
                 parent_info,
                 ignored_derived_impls,
-            }
-        } else {
-            MultipleDeadCodes::DeadCodes {
+            },
+
+            ReportOn::NamedField => MultipleDeadCodes::DeadCodes {
                 multiple,
                 num,
                 descr,
@@ -884,11 +890,11 @@ impl<'tcx> DeadVisitor<'tcx> {
                 name_list,
                 parent_info,
                 ignored_derived_impls,
-            }
+            },
         };
 
         let hir_id = tcx.local_def_id_to_hir_id(first_item.def_id);
-        self.tcx.emit_spanned_lint(lint, hir_id, MultiSpan::from_spans(spans), diag);
+        self.tcx.emit_spanned_lint(DEAD_CODE, hir_id, MultiSpan::from_spans(spans), diag);
     }
 
     fn warn_multiple(
@@ -896,7 +902,7 @@ impl<'tcx> DeadVisitor<'tcx> {
         def_id: LocalDefId,
         participle: &str,
         dead_codes: Vec<DeadItem>,
-        lint: &'static Lint,
+        report_on: ReportOn,
     ) {
         let mut dead_codes = dead_codes
             .iter()
@@ -907,7 +913,7 @@ impl<'tcx> DeadVisitor<'tcx> {
         }
         dead_codes.sort_by_key(|v| v.level);
         for group in dead_codes[..].group_by(|a, b| a.level == b.level) {
-            self.lint_at_single_level(&group, participle, Some(def_id), lint);
+            self.lint_at_single_level(&group, participle, Some(def_id), report_on);
         }
     }
 
@@ -915,9 +921,9 @@ impl<'tcx> DeadVisitor<'tcx> {
         let item = DeadItem {
             def_id: id,
             name: self.tcx.item_name(id.to_def_id()),
-            level: self.def_lint_level(DEAD_CODE, id),
+            level: self.def_lint_level(id),
         };
-        self.lint_at_single_level(&[&item], participle, None, DEAD_CODE);
+        self.lint_at_single_level(&[&item], participle, None, ReportOn::NamedField);
     }
 
     fn check_definition(&mut self, def_id: LocalDefId) {
@@ -964,12 +970,12 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
                 let def_id = item.id.owner_id.def_id;
                 if !visitor.is_live_code(def_id) {
                     let name = tcx.item_name(def_id.to_def_id());
-                    let level = visitor.def_lint_level(DEAD_CODE, def_id);
+                    let level = visitor.def_lint_level(def_id);
 
                     dead_items.push(DeadItem { def_id, name, level })
                 }
             }
-            visitor.warn_multiple(item.owner_id.def_id, "used", dead_items, DEAD_CODE);
+            visitor.warn_multiple(item.owner_id.def_id, "used", dead_items, ReportOn::NamedField);
         }
 
         if !live_symbols.contains(&item.owner_id.def_id) {
@@ -991,7 +997,7 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
                 let def_id = variant.def_id.expect_local();
                 if !live_symbols.contains(&def_id) {
                     // Record to group diagnostics.
-                    let level = visitor.def_lint_level(DEAD_CODE, def_id);
+                    let level = visitor.def_lint_level(def_id);
                     dead_variants.push(DeadItem { def_id, name: variant.name, level });
                     continue;
                 }
@@ -999,24 +1005,30 @@ fn check_mod_deathness(tcx: TyCtxt<'_>, module: LocalModDefId) {
                 let is_positional = variant.fields.raw.first().map_or(false, |field| {
                     field.name.as_str().starts_with(|c: char| c.is_ascii_digit())
                 });
-                let lint = if is_positional { UNUSED_TUPLE_STRUCT_FIELDS } else { DEAD_CODE };
+                let report_on =
+                    if is_positional { ReportOn::TupleField } else { ReportOn::NamedField };
                 let dead_fields = variant
                     .fields
                     .iter()
                     .filter_map(|field| {
                         let def_id = field.did.expect_local();
                         if let ShouldWarnAboutField::Yes = visitor.should_warn_about_field(field) {
-                            let level = visitor.def_lint_level(lint, def_id);
+                            let level = visitor.def_lint_level(def_id);
                             Some(DeadItem { def_id, name: field.name, level })
                         } else {
                             None
                         }
                     })
                     .collect();
-                visitor.warn_multiple(def_id, "read", dead_fields, lint);
+                visitor.warn_multiple(def_id, "read", dead_fields, report_on);
             }
 
-            visitor.warn_multiple(item.owner_id.def_id, "constructed", dead_variants, DEAD_CODE);
+            visitor.warn_multiple(
+                item.owner_id.def_id,
+                "constructed",
+                dead_variants,
+                ReportOn::NamedField,
+            );
         }
     }
 
diff --git a/compiler/rustc_passes/src/debugger_visualizer.rs b/compiler/rustc_passes/src/debugger_visualizer.rs
index f4cd2fbc91f..42e929bde2c 100644
--- a/compiler/rustc_passes/src/debugger_visualizer.rs
+++ b/compiler/rustc_passes/src/debugger_visualizer.rs
@@ -48,7 +48,7 @@ impl DebuggerVisualizerCollector<'_> {
             let file =
                 match resolve_path(&self.sess.parse_sess, visualizer_path.as_str(), attr.span) {
                     Ok(file) => file,
-                    Err(mut err) => {
+                    Err(err) => {
                         err.emit();
                         return;
                     }
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 856256a0641..cf3c7cc4ace 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -813,6 +813,12 @@ pub struct UnknownExternLangItem {
 pub struct MissingPanicHandler;
 
 #[derive(Diagnostic)]
+#[diag(passes_panic_unwind_without_std)]
+#[help]
+#[note]
+pub struct PanicUnwindWithoutStd;
+
+#[derive(Diagnostic)]
 #[diag(passes_missing_lang_item)]
 #[note]
 #[help]
@@ -868,8 +874,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidAttrAtCrateLevel {
     fn into_diagnostic(self, dcx: &'_ DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag =
             DiagnosticBuilder::new(dcx, level, fluent::passes_invalid_attr_at_crate_level);
-        diag.set_span(self.span);
-        diag.set_arg("name", self.name);
+        diag.span(self.span);
+        diag.arg("name", self.name);
         // Only emit an error with a suggestion if we can create a string out
         // of the attribute span
         if let Some(span) = self.sugg_span {
@@ -881,7 +887,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidAttrAtCrateLevel {
             );
         }
         if let Some(item) = self.item {
-            diag.set_arg("kind", item.kind);
+            diag.arg("kind", item.kind);
             diag.span_label(item.span, fluent::passes_invalid_attr_at_crate_level_item);
         }
         diag
@@ -1018,9 +1024,9 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'_, G> for BreakNonLoop<'a> {
     #[track_caller]
     fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_break_non_loop);
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag.code(error_code!(E0571));
-        diag.set_arg("kind", self.kind);
+        diag.arg("kind", self.kind);
         diag.span_label(self.span, fluent::passes_label);
         if let Some(head) = self.head {
             diag.span_label(head, fluent::passes_label2);
@@ -1040,7 +1046,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'_, G> for BreakNonLoop<'a> {
                     // This error is redundant, we will have already emitted a
                     // suggestion to use the label when `segment` wasn't found
                     // (hence the `Res::Err` check).
-                    diag.delay_as_bug();
+                    diag.downgrade_to_delayed_bug();
                 }
                 _ => {
                     diag.span_suggestion(
@@ -1162,7 +1168,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NakedFunctionsAsmBlock {
     #[track_caller]
     fn into_diagnostic(self, dcx: &DiagCtxt, level: Level) -> DiagnosticBuilder<'_, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_naked_functions_asm_block);
-        diag.set_span(self.span);
+        diag.span(self.span);
         diag.code(error_code!(E0787));
         for span in self.multiple_asms.iter() {
             diag.span_label(*span, fluent::passes_label_multiple_asm);
@@ -1273,11 +1279,11 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for NoMainErr {
     #[track_caller]
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
         let mut diag = DiagnosticBuilder::new(dcx, level, fluent::passes_no_main_function);
-        diag.set_span(DUMMY_SP);
+        diag.span(DUMMY_SP);
         diag.code(error_code!(E0601));
-        diag.set_arg("crate_name", self.crate_name);
-        diag.set_arg("filename", self.filename);
-        diag.set_arg("has_filename", self.has_filename);
+        diag.arg("crate_name", self.crate_name);
+        diag.arg("filename", self.filename);
+        diag.arg("has_filename", self.has_filename);
         let note = if !self.non_main_fns.is_empty() {
             for &span in &self.non_main_fns {
                 diag.span_note(span, fluent::passes_here_is_main);
@@ -1294,7 +1300,7 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for NoMainErr {
         if self.file_empty {
             diag.note(note);
         } else {
-            diag.set_span(self.sp.shrink_to_hi());
+            diag.span(self.sp.shrink_to_hi());
             diag.span_label(self.sp.shrink_to_hi(), note);
         }
 
@@ -1340,15 +1346,15 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for DuplicateLangItem {
             },
         );
         diag.code(error_code!(E0152));
-        diag.set_arg("lang_item_name", self.lang_item_name);
-        diag.set_arg("crate_name", self.crate_name);
-        diag.set_arg("dependency_of", self.dependency_of);
-        diag.set_arg("path", self.path);
-        diag.set_arg("orig_crate_name", self.orig_crate_name);
-        diag.set_arg("orig_dependency_of", self.orig_dependency_of);
-        diag.set_arg("orig_path", self.orig_path);
+        diag.arg("lang_item_name", self.lang_item_name);
+        diag.arg("crate_name", self.crate_name);
+        diag.arg("dependency_of", self.dependency_of);
+        diag.arg("path", self.path);
+        diag.arg("orig_crate_name", self.orig_crate_name);
+        diag.arg("orig_dependency_of", self.orig_dependency_of);
+        diag.arg("orig_path", self.orig_path);
         if let Some(span) = self.local_span {
-            diag.set_span(span);
+            diag.span(span);
         }
         if let Some(span) = self.first_defined_span {
             diag.span_note(span, fluent::passes_first_defined_span);
diff --git a/compiler/rustc_passes/src/hir_id_validator.rs b/compiler/rustc_passes/src/hir_id_validator.rs
index 0727bad7c5a..02f56ecb10b 100644
--- a/compiler/rustc_passes/src/hir_id_validator.rs
+++ b/compiler/rustc_passes/src/hir_id_validator.rs
@@ -31,7 +31,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
 
         if !errors.is_empty() {
             let message = errors.iter().fold(String::new(), |s1, s2| s1 + "\n" + s2);
-            tcx.dcx().span_delayed_bug(rustc_span::DUMMY_SP, message);
+            tcx.dcx().delayed_bug(message);
         }
     }
 }
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index cfe829f170f..92687c705ae 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -98,7 +98,6 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::DUMMY_SP;
 use rustc_span::{BytePos, Span};
 
 use std::io;
@@ -351,10 +350,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
     }
 
     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
-        self.add_from_pat(arm.pat);
-        if let Some(hir::Guard::IfLet(let_expr)) = arm.guard {
-            self.add_from_pat(let_expr.pat);
-        }
+        self.add_from_pat(&arm.pat);
         intravisit::walk_arm(self, arm);
     }
 
@@ -563,7 +559,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
         match self.successors[ln] {
             Some(successor) => self.assigned_on_entry(successor, var),
             None => {
-                self.ir.tcx.dcx().span_delayed_bug(DUMMY_SP, "no successor");
+                self.ir.tcx.dcx().delayed_bug("no successor");
                 true
             }
         }
@@ -921,14 +917,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
                 for arm in arms {
                     let body_succ = self.propagate_through_expr(arm.body, succ);
 
-                    let guard_succ = arm.guard.as_ref().map_or(body_succ, |g| match g {
-                        hir::Guard::If(e) => self.propagate_through_expr(e, body_succ),
-                        hir::Guard::IfLet(let_expr) => {
-                            let let_bind = self.define_bindings_in_pat(let_expr.pat, body_succ);
-                            self.propagate_through_expr(let_expr.init, let_bind)
-                        }
-                    });
-                    let arm_succ = self.define_bindings_in_pat(arm.pat, guard_succ);
+                    let guard_succ = arm
+                        .guard
+                        .as_ref()
+                        .map_or(body_succ, |g| self.propagate_through_expr(g, body_succ));
+                    let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
                     self.merge_from_succ(ln, arm_succ);
                 }
                 self.propagate_through_expr(e, ln)
@@ -1328,9 +1321,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
 
     fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
         self.check_unused_vars_in_pat(arm.pat, None, None, |_, _, _, _| {});
-        if let Some(hir::Guard::IfLet(let_expr)) = arm.guard {
-            self.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {});
-        }
         intravisit::walk_arm(self, arm);
     }
 }
@@ -1630,7 +1620,7 @@ impl<'tcx> Liveness<'_, 'tcx> {
                     let from_macro = non_shorthands
                         .iter()
                         .find(|(_, pat_span, ident_span)| {
-                            pat_span.ctxt() != ident_span.ctxt() && pat_span.from_expansion()
+                            !pat_span.eq_ctxt(*ident_span) && pat_span.from_expansion()
                         })
                         .map(|(_, pat_span, _)| *pat_span);
                     let non_shorthands = non_shorthands
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 87fdedc15ba..18b9ba0f042 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -6,7 +6,8 @@ use rustc_attr::{
     self as attr, ConstStability, DeprecatedSince, Stability, StabilityLevel, StableSince,
     Unstable, UnstableReason, VERSION_PLACEHOLDER,
 };
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_data_structures::unord::{ExtendUnord, UnordMap, UnordSet};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, LocalModDefId, CRATE_DEF_ID, LOCAL_CRATE};
@@ -923,7 +924,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     }
 
     let declared_lang_features = &tcx.features().declared_lang_features;
-    let mut lang_features = FxHashSet::default();
+    let mut lang_features = UnordSet::default();
     for &(feature, span, since) in declared_lang_features {
         if let Some(since) = since {
             // Warn if the user has enabled an already-stable lang feature.
@@ -980,11 +981,11 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     fn check_features<'tcx>(
         tcx: TyCtxt<'tcx>,
         remaining_lib_features: &mut FxIndexMap<&Symbol, Span>,
-        remaining_implications: &mut FxHashMap<Symbol, Symbol>,
+        remaining_implications: &mut UnordMap<Symbol, Symbol>,
         defined_features: &LibFeatures,
-        all_implications: &FxHashMap<Symbol, Symbol>,
+        all_implications: &UnordMap<Symbol, Symbol>,
     ) {
-        for (feature, since) in defined_features.to_vec() {
+        for (feature, since) in defined_features.to_sorted_vec() {
             if let FeatureStability::AcceptedSince(since) = since
                 && let Some(span) = remaining_lib_features.get(&feature)
             {
@@ -1021,7 +1022,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         // `remaining_lib_features`.
         let mut all_implications = remaining_implications.clone();
         for &cnum in tcx.crates(()) {
-            all_implications.extend(tcx.stability_implications(cnum));
+            all_implications
+                .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
         }
 
         check_features(
@@ -1050,10 +1052,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         tcx.dcx().emit_err(errors::UnknownFeature { span, feature: *feature });
     }
 
-    // We only use the hash map contents to emit errors, and the order of
-    // emitted errors do not affect query stability.
-    #[allow(rustc::potential_query_instability)]
-    for (implied_by, feature) in remaining_implications {
+    for (&implied_by, &feature) in remaining_implications.to_sorted_stable_ord() {
         let local_defined_features = tcx.lib_features(LOCAL_CRATE);
         let span = local_defined_features
             .stability
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index db3d442676e..4eb0c6c275e 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -9,7 +9,9 @@ use rustc_middle::middle::lang_items::required;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::CrateType;
 
-use crate::errors::{MissingLangItem, MissingPanicHandler, UnknownExternLangItem};
+use crate::errors::{
+    MissingLangItem, MissingPanicHandler, PanicUnwindWithoutStd, UnknownExternLangItem,
+};
 
 /// Checks the crate for usage of weak lang items, returning a vector of all the
 /// language items required by this crate, but not defined yet.
@@ -76,6 +78,8 @@ fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
         if missing.contains(&item) && required(tcx, item) && items.get(item).is_none() {
             if item == LangItem::PanicImpl {
                 tcx.dcx().emit_err(MissingPanicHandler);
+            } else if item == LangItem::EhPersonality {
+                tcx.dcx().emit_err(PanicUnwindWithoutStd);
             } else {
                 tcx.dcx().emit_err(MissingLangItem { name: item.name() });
             }
diff --git a/compiler/rustc_pattern_analysis/src/lib.rs b/compiler/rustc_pattern_analysis/src/lib.rs
index e01b571ede1..b52643adcc9 100644
--- a/compiler/rustc_pattern_analysis/src/lib.rs
+++ b/compiler/rustc_pattern_analysis/src/lib.rs
@@ -24,6 +24,8 @@ use std::fmt;
 use rustc_index::Idx;
 #[cfg(feature = "rustc")]
 use rustc_middle::ty::Ty;
+#[cfg(feature = "rustc")]
+use rustc_span::ErrorGuaranteed;
 
 use crate::constructor::{Constructor, ConstructorSet};
 #[cfg(feature = "rustc")]
@@ -52,6 +54,8 @@ impl<'a, T: ?Sized> Captures<'a> for T {}
 pub trait TypeCx: Sized + fmt::Debug {
     /// The type of a pattern.
     type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
+    /// Errors that can abort analysis.
+    type Error: fmt::Debug;
     /// The index of an enum variant.
     type VariantIdx: Clone + Idx;
     /// A string literal
@@ -61,8 +65,6 @@ pub trait TypeCx: Sized + fmt::Debug {
     /// Extra data to store in a pattern.
     type PatData: Clone;
 
-    /// FIXME(Nadrieril): `Cx` should only give us revealed types.
-    fn reveal_opaque_ty(&self, ty: Self::Ty) -> Self::Ty;
     fn is_exhaustive_patterns_feature_on(&self) -> bool;
 
     /// The number of fields for this constructor.
@@ -75,7 +77,7 @@ pub trait TypeCx: Sized + fmt::Debug {
     /// The set of all the constructors for `ty`.
     ///
     /// This must follow the invariants of `ConstructorSet`
-    fn ctors_for_ty(&self, ty: Self::Ty) -> ConstructorSet<Self>;
+    fn ctors_for_ty(&self, ty: Self::Ty) -> Result<ConstructorSet<Self>, Self::Error>;
 
     /// Best-effort `Debug` implementation.
     fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result;
@@ -111,24 +113,25 @@ pub fn analyze_match<'p, 'tcx>(
     tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
     arms: &[rustc::MatchArm<'p, 'tcx>],
     scrut_ty: Ty<'tcx>,
-) -> rustc::UsefulnessReport<'p, 'tcx> {
+) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
     // Arena to store the extra wildcards we construct during analysis.
     let wildcard_arena = tycx.pattern_arena;
+    let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
     let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
     let cx = MatchCtxt { tycx, wildcard_arena };
 
-    let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity);
+    let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity)?;
 
     let pat_column = PatternColumn::new(arms);
 
     // Lint on ranges that overlap on their endpoints, which is likely a mistake.
-    lint_overlapping_range_endpoints(cx, &pat_column);
+    lint_overlapping_range_endpoints(cx, &pat_column)?;
 
     // Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
     // `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
     if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
-        lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)
+        lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)?;
     }
 
-    report
+    Ok(report)
 }
diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs
index cb712fe640c..52c9af85006 100644
--- a/compiler/rustc_pattern_analysis/src/lints.rs
+++ b/compiler/rustc_pattern_analysis/src/lints.rs
@@ -1,21 +1,21 @@
 use smallvec::SmallVec;
 
 use rustc_data_structures::captures::Captures;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty;
 use rustc_session::lint;
 use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
-use rustc_span::Span;
+use rustc_span::{ErrorGuaranteed, Span};
 
 use crate::constructor::{IntRange, MaybeInfiniteInt};
 use crate::errors::{
     NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
     OverlappingRangeEndpoints, Uncovered,
 };
+use crate::pat::PatOrWild;
 use crate::rustc::{
-    Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
+    Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RevealedTy, RustcMatchCheckCtxt,
     SplitConstructorSet, WitnessPat,
 };
-use crate::TypeCx;
 
 /// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
 /// inspect the same subvalue/place".
@@ -24,9 +24,9 @@ use crate::TypeCx;
 /// the depth of patterns, whereas `compute_exhaustiveness_and_usefulness` is worst-case exponential
 /// (exhaustiveness is NP-complete). The core difference is that we treat sub-columns separately.
 ///
-/// This must not contain an or-pattern. `specialize` takes care to expand them.
+/// This must not contain an or-pattern. `expand_and_push` takes care to expand them.
 ///
-/// This is not used in the main algorithm; only in lints.
+/// This is not used in the usefulness algorithm; only in lints.
 #[derive(Debug)]
 pub(crate) struct PatternColumn<'p, 'tcx> {
     patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
@@ -34,34 +34,38 @@ pub(crate) struct PatternColumn<'p, 'tcx> {
 
 impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
     pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self {
-        let mut patterns = Vec::with_capacity(arms.len());
+        let patterns = Vec::with_capacity(arms.len());
+        let mut column = PatternColumn { patterns };
         for arm in arms {
-            if arm.pat.is_or_pat() {
-                patterns.extend(arm.pat.flatten_or_pat())
-            } else {
-                patterns.push(arm.pat)
-            }
+            column.expand_and_push(PatOrWild::Pat(arm.pat));
         }
-        Self { patterns }
-    }
-
-    fn is_empty(&self) -> bool {
-        self.patterns.is_empty()
+        column
     }
-    fn head_ty(&self, cx: MatchCtxt<'_, 'p, 'tcx>) -> Option<Ty<'tcx>> {
-        if self.patterns.len() == 0 {
-            return None;
+    /// Pushes a pattern onto the column, expanding any or-patterns into its subpatterns.
+    /// Internal method, prefer [`PatternColumn::new`].
+    fn expand_and_push(&mut self, pat: PatOrWild<'p, RustcMatchCheckCtxt<'p, 'tcx>>) {
+        // We flatten or-patterns and skip algorithm-generated wildcards.
+        if pat.is_or_pat() {
+            self.patterns.extend(
+                pat.flatten_or_pat().into_iter().filter_map(|pat_or_wild| pat_or_wild.as_pat()),
+            )
+        } else if let Some(pat) = pat.as_pat() {
+            self.patterns.push(pat)
         }
+    }
 
-        let ty = self.patterns[0].ty();
-        // FIXME(Nadrieril): `Cx` should only give us revealed types.
-        Some(cx.tycx.reveal_opaque_ty(ty))
+    fn head_ty(&self) -> Option<RevealedTy<'tcx>> {
+        self.patterns.first().map(|pat| pat.ty())
     }
 
     /// Do constructor splitting on the constructors of the column.
-    fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> {
+    fn analyze_ctors(
+        &self,
+        pcx: &PlaceCtxt<'_, 'p, 'tcx>,
+    ) -> Result<SplitConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
         let column_ctors = self.patterns.iter().map(|p| p.ctor());
-        pcx.ctors_for_ty().split(pcx, column_ctors)
+        let ctors_for_ty = &pcx.ctors_for_ty()?;
+        Ok(ctors_for_ty.split(pcx, column_ctors))
     }
 
     fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'_> {
@@ -91,21 +95,11 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
         let relevant_patterns =
             self.patterns.iter().filter(|pat| ctor.is_covered_by(pcx, pat.ctor()));
         for pat in relevant_patterns {
-            let specialized = pat.specialize(pcx, ctor);
-            for (subpat, column) in specialized.iter().zip(&mut specialized_columns) {
-                if subpat.is_or_pat() {
-                    column.patterns.extend(subpat.flatten_or_pat())
-                } else {
-                    column.patterns.push(subpat)
-                }
+            let specialized = pat.specialize(ctor, arity);
+            for (subpat, column) in specialized.into_iter().zip(&mut specialized_columns) {
+                column.expand_and_push(subpat);
             }
         }
-
-        assert!(
-            !specialized_columns[0].is_empty(),
-            "ctor {ctor:?} was listed as present but isn't;
-            there is an inconsistency between `Constructor::is_covered_by` and `ConstructorSet::split`"
-        );
         specialized_columns
     }
 }
@@ -116,18 +110,18 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
 fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
     cx: MatchCtxt<'a, 'p, 'tcx>,
     column: &PatternColumn<'p, 'tcx>,
-) -> Vec<WitnessPat<'p, 'tcx>> {
-    let Some(ty) = column.head_ty(cx) else {
-        return Vec::new();
+) -> Result<Vec<WitnessPat<'p, 'tcx>>, ErrorGuaranteed> {
+    let Some(ty) = column.head_ty() else {
+        return Ok(Vec::new());
     };
     let pcx = &PlaceCtxt::new_dummy(cx, ty);
 
-    let set = column.analyze_ctors(pcx);
+    let set = column.analyze_ctors(pcx)?;
     if set.present.is_empty() {
         // We can't consistently handle the case where no constructors are present (since this would
         // require digging deep through any type in case there's a non_exhaustive enum somewhere),
         // so for consistency we refuse to handle the top-level case, where we could handle it.
-        return vec![];
+        return Ok(Vec::new());
     }
 
     let mut witnesses = Vec::new();
@@ -147,7 +141,7 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
         let wild_pat = WitnessPat::wild_from_ctor(pcx, ctor);
         for (i, col_i) in specialized_columns.iter().enumerate() {
             // Compute witnesses for each column.
-            let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i);
+            let wits_for_col_i = collect_nonexhaustive_missing_variants(cx, col_i)?;
             // For each witness, we build a new pattern in the shape of `ctor(_, _, wit, _, _)`,
             // adding enough wildcards to match `arity`.
             for wit in wits_for_col_i {
@@ -157,21 +151,21 @@ fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
             }
         }
     }
-    witnesses
+    Ok(witnesses)
 }
 
 pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
     cx: MatchCtxt<'a, 'p, 'tcx>,
     arms: &[MatchArm<'p, 'tcx>],
     pat_column: &PatternColumn<'p, 'tcx>,
-    scrut_ty: Ty<'tcx>,
-) {
+    scrut_ty: RevealedTy<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
     let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
     if !matches!(
         rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
         rustc_session::lint::Level::Allow
     ) {
-        let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column);
+        let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column)?;
         if !witnesses.is_empty() {
             // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
             // is not exhaustive enough.
@@ -182,7 +176,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
                 rcx.match_lint_level,
                 rcx.scrut_span,
                 NonExhaustiveOmittedPattern {
-                    scrut_ty,
+                    scrut_ty: scrut_ty.inner(),
                     uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
                 },
             );
@@ -204,12 +198,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
 
                 use rustc_errors::DecorateLint;
                 let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().unwrap().span, "");
-                err.set_primary_message(decorator.msg());
+                err.primary_message(decorator.msg());
                 decorator.decorate_lint(&mut err);
                 err.emit();
             }
         }
     }
+    Ok(())
 }
 
 /// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
@@ -217,14 +212,14 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
 pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
     cx: MatchCtxt<'a, 'p, 'tcx>,
     column: &PatternColumn<'p, 'tcx>,
-) {
-    let Some(ty) = column.head_ty(cx) else {
-        return;
+) -> Result<(), ErrorGuaranteed> {
+    let Some(ty) = column.head_ty() else {
+        return Ok(());
     };
     let pcx = &PlaceCtxt::new_dummy(cx, ty);
     let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
 
-    let set = column.analyze_ctors(pcx);
+    let set = column.analyze_ctors(pcx)?;
 
     if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
         let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
@@ -281,8 +276,9 @@ pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
         // Recurse into the fields.
         for ctor in set.present {
             for col in column.specialize(pcx, &ctor) {
-                lint_overlapping_range_endpoints(cx, &col);
+                lint_overlapping_range_endpoints(cx, &col)?;
             }
         }
     }
+    Ok(())
 }
diff --git a/compiler/rustc_pattern_analysis/src/pat.rs b/compiler/rustc_pattern_analysis/src/pat.rs
index db41d2824a1..2f5dc241cb7 100644
--- a/compiler/rustc_pattern_analysis/src/pat.rs
+++ b/compiler/rustc_pattern_analysis/src/pat.rs
@@ -50,14 +50,6 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
     pub(crate) fn is_or_pat(&self) -> bool {
         matches!(self.ctor, Or)
     }
-    /// Expand this (possibly-nested) or-pattern into its alternatives.
-    pub(crate) fn flatten_or_pat(&self) -> SmallVec<[&Self; 1]> {
-        if self.is_or_pat() {
-            self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect()
-        } else {
-            smallvec![self]
-        }
-    }
 
     pub fn ctor(&self) -> &Constructor<Cx> {
         &self.ctor
@@ -79,16 +71,10 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
     /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
     pub(crate) fn specialize(
         &self,
-        pcx: &PlaceCtxt<'_, 'p, Cx>,
         other_ctor: &Constructor<Cx>,
-    ) -> SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]> {
-        let wildcard_sub_tys = || {
-            let tys = pcx.ctor_sub_tys(other_ctor);
-            tys.iter()
-                .map(|ty| DeconstructedPat::wildcard(*ty))
-                .map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
-                .collect()
-        };
+        ctor_arity: usize,
+    ) -> SmallVec<[PatOrWild<'p, Cx>; 2]> {
+        let wildcard_sub_tys = || (0..ctor_arity).map(|_| PatOrWild::Wild).collect();
         match (&self.ctor, other_ctor) {
             // Return a wildcard for each field of `other_ctor`.
             (Wildcard, _) => wildcard_sub_tys(),
@@ -104,14 +90,15 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
                 // Fill in the fields from both ends.
                 let new_arity = fields.len();
                 for i in 0..prefix {
-                    fields[i] = &self.fields[i];
+                    fields[i] = PatOrWild::Pat(&self.fields[i]);
                 }
                 for i in 0..suffix {
-                    fields[new_arity - 1 - i] = &self.fields[self.fields.len() - 1 - i];
+                    fields[new_arity - 1 - i] =
+                        PatOrWild::Pat(&self.fields[self.fields.len() - 1 - i]);
                 }
                 fields
             }
-            _ => self.fields.iter().collect(),
+            _ => self.fields.iter().map(PatOrWild::Pat).collect(),
         }
     }
 
@@ -152,14 +139,86 @@ impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
     }
 }
 
-/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
-/// `Display` impl.
+/// This is best effort and not good enough for a `Display` impl.
 impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Cx::debug_pat(f, self)
     }
 }
 
+/// Represents either a pattern obtained from user input or a wildcard constructed during the
+/// algorithm. Do not use `Wild` to represent a wildcard pattern comping from user input.
+///
+/// This is morally `Option<&'p DeconstructedPat>` where `None` is interpreted as a wildcard.
+#[derive(derivative::Derivative)]
+#[derivative(Clone(bound = ""), Copy(bound = ""))]
+pub(crate) enum PatOrWild<'p, Cx: TypeCx> {
+    /// A non-user-provided wildcard, created during specialization.
+    Wild,
+    /// A user-provided pattern.
+    Pat(&'p DeconstructedPat<'p, Cx>),
+}
+
+impl<'p, Cx: TypeCx> PatOrWild<'p, Cx> {
+    pub(crate) fn as_pat(&self) -> Option<&'p DeconstructedPat<'p, Cx>> {
+        match self {
+            PatOrWild::Wild => None,
+            PatOrWild::Pat(pat) => Some(pat),
+        }
+    }
+    pub(crate) fn ctor(self) -> &'p Constructor<Cx> {
+        match self {
+            PatOrWild::Wild => &Wildcard,
+            PatOrWild::Pat(pat) => pat.ctor(),
+        }
+    }
+
+    pub(crate) fn is_or_pat(&self) -> bool {
+        match self {
+            PatOrWild::Wild => false,
+            PatOrWild::Pat(pat) => pat.is_or_pat(),
+        }
+    }
+
+    /// Expand this (possibly-nested) or-pattern into its alternatives.
+    pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> {
+        match self {
+            PatOrWild::Pat(pat) if pat.is_or_pat() => {
+                pat.iter_fields().flat_map(|p| PatOrWild::Pat(p).flatten_or_pat()).collect()
+            }
+            _ => smallvec![self],
+        }
+    }
+
+    /// Specialize this pattern with a constructor.
+    /// `other_ctor` can be different from `self.ctor`, but must be covered by it.
+    pub(crate) fn specialize(
+        &self,
+        other_ctor: &Constructor<Cx>,
+        ctor_arity: usize,
+    ) -> SmallVec<[PatOrWild<'p, Cx>; 2]> {
+        match self {
+            PatOrWild::Wild => (0..ctor_arity).map(|_| PatOrWild::Wild).collect(),
+            PatOrWild::Pat(pat) => pat.specialize(other_ctor, ctor_arity),
+        }
+    }
+
+    pub(crate) fn set_useful(&self) {
+        if let PatOrWild::Pat(pat) = self {
+            pat.set_useful()
+        }
+    }
+}
+
+impl<'p, Cx: TypeCx> fmt::Debug for PatOrWild<'p, Cx> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            PatOrWild::Wild => write!(f, "_"),
+            PatOrWild::Pat(pat) => pat.fmt(f),
+        }
+    }
+}
+
 /// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
 /// purposes. As such they don't use interning and can be cloned.
 #[derive(derivative::Derivative)]
diff --git a/compiler/rustc_pattern_analysis/src/rustc.rs b/compiler/rustc_pattern_analysis/src/rustc.rs
index b09d565f076..a8d1bece613 100644
--- a/compiler/rustc_pattern_analysis/src/rustc.rs
+++ b/compiler/rustc_pattern_analysis/src/rustc.rs
@@ -12,7 +12,9 @@ use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::mir::{self, Const};
 use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
 use rustc_middle::ty::layout::IntegerExt;
+use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, VariantDef};
+use rustc_span::ErrorGuaranteed;
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
 use smallvec::SmallVec;
@@ -41,6 +43,30 @@ pub type UsefulnessReport<'p, 'tcx> =
     crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
 pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, 'tcx>>;
 
+/// A type which has gone through `cx.reveal_opaque_ty`, i.e. if it was opaque it was replaced by
+/// the hidden type if allowed in the current body. This ensures we consistently inspect the hidden
+/// types when we should.
+///
+/// Use `.inner()` or deref to get to the `Ty<'tcx>`.
+#[repr(transparent)]
+#[derive(derivative::Derivative)]
+#[derive(Clone, Copy)]
+#[derivative(Debug = "transparent")]
+pub struct RevealedTy<'tcx>(Ty<'tcx>);
+
+impl<'tcx> std::ops::Deref for RevealedTy<'tcx> {
+    type Target = Ty<'tcx>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<'tcx> RevealedTy<'tcx> {
+    pub fn inner(self) -> Ty<'tcx> {
+        self.0
+    }
+}
+
 #[derive(Clone)]
 pub struct RustcMatchCheckCtxt<'p, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
@@ -74,20 +100,48 @@ impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> {
 }
 
 impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
-    fn reveal_opaque(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
+    /// Type inference occasionally gives us opaque types in places where corresponding patterns
+    /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
+    /// types, we use the corresponding concrete type if possible.
+    #[inline]
+    pub fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> RevealedTy<'tcx> {
+        fn reveal_inner<'tcx>(
+            cx: &RustcMatchCheckCtxt<'_, 'tcx>,
+            ty: Ty<'tcx>,
+        ) -> RevealedTy<'tcx> {
+            let ty::Alias(ty::Opaque, alias_ty) = *ty.kind() else { bug!() };
+            if let Some(local_def_id) = alias_ty.def_id.as_local() {
+                let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
+                if let Some(ty) = cx.reveal_opaque_key(key) {
+                    return RevealedTy(ty);
+                }
+            }
+            RevealedTy(ty)
+        }
+        if let ty::Alias(ty::Opaque, _) = ty.kind() {
+            reveal_inner(self, ty)
+        } else {
+            RevealedTy(ty)
+        }
+    }
+
+    /// Returns the hidden type corresponding to this key if the body under analysis is allowed to
+    /// know it.
+    fn reveal_opaque_key(&self, key: OpaqueTypeKey<'tcx>) -> Option<Ty<'tcx>> {
         self.typeck_results.concrete_opaque_types.get(&key).map(|x| x.ty)
     }
+    // This can take a non-revealed `Ty` because it reveals opaques itself.
     pub fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
         !ty.inhabited_predicate(self.tcx).apply_revealing_opaque(
             self.tcx,
             self.param_env,
             self.module,
-            &|key| self.reveal_opaque(key),
+            &|key| self.reveal_opaque_key(key),
         )
     }
 
     /// Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`.
-    pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool {
+    pub fn is_foreign_non_exhaustive_enum(&self, ty: RevealedTy<'tcx>) -> bool {
         match ty.kind() {
             ty::Adt(def, ..) => {
                 def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did().is_local()
@@ -98,7 +152,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
 
     /// Whether the range denotes the fictitious values before `isize::MIN` or after
     /// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist).
-    pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool {
+    pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> bool {
         ty.is_ptr_sized_integral() && {
             // The two invalid ranges are `NegInfinity..isize::MIN` (represented as
             // `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy`
@@ -110,29 +164,14 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
         }
     }
 
-    /// Type inference occasionally gives us opaque types in places where corresponding patterns
-    /// have more specific types. To avoid inconsistencies as well as detect opaque uninhabited
-    /// types, we use the corresponding concrete type if possible.
-    fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if let ty::Alias(ty::Opaque, alias_ty) = ty.kind() {
-            if let Some(local_def_id) = alias_ty.def_id.as_local() {
-                let key = ty::OpaqueTypeKey { def_id: local_def_id, args: alias_ty.args };
-                if let Some(real_ty) = self.typeck_results.concrete_opaque_types.get(&key) {
-                    return real_ty.ty;
-                }
-            }
-        }
-        ty
-    }
-
     // In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
     // uninhabited fields in order not to reveal the uninhabitedness of the whole variant.
     // This lists the fields we keep along with their types.
     pub(crate) fn list_variant_nonhidden_fields(
         &self,
-        ty: Ty<'tcx>,
+        ty: RevealedTy<'tcx>,
         variant: &'tcx VariantDef,
-    ) -> impl Iterator<Item = (FieldIdx, Ty<'tcx>)> + Captures<'p> + Captures<'_> {
+    ) -> impl Iterator<Item = (FieldIdx, RevealedTy<'tcx>)> + Captures<'p> + Captures<'_> {
         let cx = self;
         let ty::Adt(adt, args) = ty.kind() else { bug!() };
         // Whether we must not match the fields of this variant exhaustively.
@@ -148,6 +187,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
             if is_uninhabited && (!is_visible || is_non_exhaustive) {
                 None
             } else {
+                let ty = cx.reveal_opaque_ty(ty);
                 Some((FieldIdx::new(i), ty))
             }
         })
@@ -170,16 +210,26 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     /// Returns the types of the fields for a given constructor. The result must have a length of
     /// `ctor.arity()`.
     #[instrument(level = "trace", skip(self))]
-    pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] {
+    pub(crate) fn ctor_sub_tys(
+        &self,
+        ctor: &Constructor<'p, 'tcx>,
+        ty: RevealedTy<'tcx>,
+    ) -> &[RevealedTy<'tcx>] {
+        fn reveal_and_alloc<'a, 'tcx>(
+            cx: &'a RustcMatchCheckCtxt<'_, 'tcx>,
+            iter: impl Iterator<Item = Ty<'tcx>>,
+        ) -> &'a [RevealedTy<'tcx>] {
+            cx.dropless_arena.alloc_from_iter(iter.map(|ty| cx.reveal_opaque_ty(ty)))
+        }
         let cx = self;
         match ctor {
             Struct | Variant(_) | UnionField => match ty.kind() {
-                ty::Tuple(fs) => cx.dropless_arena.alloc_from_iter(fs.iter()),
+                ty::Tuple(fs) => reveal_and_alloc(cx, fs.iter()),
                 ty::Adt(adt, args) => {
                     if adt.is_box() {
                         // The only legal patterns of type `Box` (outside `std`) are `_` and box
                         // patterns. If we're here we can assume this is a box pattern.
-                        cx.dropless_arena.alloc_from_iter(once(args.type_at(0)))
+                        reveal_and_alloc(cx, once(args.type_at(0)))
                     } else {
                         let variant =
                             &adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
@@ -190,13 +240,13 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 _ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
             },
             Ref => match ty.kind() {
-                ty::Ref(_, rty, _) => cx.dropless_arena.alloc_from_iter(once(*rty)),
+                ty::Ref(_, rty, _) => reveal_and_alloc(cx, once(*rty)),
                 _ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
             },
             Slice(slice) => match *ty.kind() {
                 ty::Slice(ty) | ty::Array(ty, _) => {
                     let arity = slice.arity();
-                    cx.dropless_arena.alloc_from_iter((0..arity).map(|_| ty))
+                    reveal_and_alloc(cx, (0..arity).map(|_| ty))
                 }
                 _ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
             },
@@ -217,7 +267,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     }
 
     /// The number of fields for this constructor.
-    pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> usize {
+    pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: RevealedTy<'tcx>) -> usize {
         match ctor {
             Struct | Variant(_) | UnionField => match ty.kind() {
                 ty::Tuple(fs) => fs.len(),
@@ -254,7 +304,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     ///
     /// See [`crate::constructor`] for considerations of emptiness.
     #[instrument(level = "debug", skip(self), ret)]
-    pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet<'p, 'tcx> {
+    pub fn ctors_for_ty(
+        &self,
+        ty: RevealedTy<'tcx>,
+    ) -> Result<ConstructorSet<'p, 'tcx>, ErrorGuaranteed> {
         let cx = self;
         let make_uint_range = |start, end| {
             IntRange::from_range(
@@ -263,9 +316,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 RangeEnd::Included,
             )
         };
+        // Abort on type error.
+        ty.error_reported()?;
         // This determines the set of all possible constructors for the type `ty`. For numbers,
         // arrays and slices we use ranges and variable-length slices when appropriate.
-        match ty.kind() {
+        Ok(match ty.kind() {
             ty::Bool => ConstructorSet::Bool,
             ty::Char => {
                 // The valid Unicode Scalar Value ranges.
@@ -328,7 +383,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                             .inhabited_predicate(cx.tcx, *def)
                             .instantiate(cx.tcx, args)
                             .apply_revealing_opaque(cx.tcx, cx.param_env, cx.module, &|key| {
-                                cx.reveal_opaque(key)
+                                cx.reveal_opaque_key(key)
                             });
                         // Variants that depend on a disabled unstable feature.
                         let is_unstable = matches!(
@@ -353,7 +408,9 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 }
             }
             ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
-            ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) },
+            ty::Adt(..) | ty::Tuple(..) => {
+                ConstructorSet::Struct { empty: cx.is_uninhabited(ty.inner()) }
+            }
             ty::Ref(..) => ConstructorSet::Ref,
             ty::Never => ConstructorSet::NoConstructors,
             // This type is one for which we cannot list constructors, like `str` or `f64`.
@@ -373,13 +430,13 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
             ty::CoroutineWitness(_, _) | ty::Bound(_, _) | ty::Placeholder(_) | ty::Infer(_) => {
                 bug!("Encountered unexpected type in `ConstructorSet::for_ty`: {ty:?}")
             }
-        }
+        })
     }
 
     pub(crate) fn lower_pat_range_bdy(
         &self,
         bdy: PatRangeBoundary<'tcx>,
-        ty: Ty<'tcx>,
+        ty: RevealedTy<'tcx>,
     ) -> MaybeInfiniteInt {
         match bdy {
             PatRangeBoundary::NegInfinity => MaybeInfiniteInt::NegInfinity,
@@ -402,6 +459,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     pub fn lower_pat(&self, pat: &'p Pat<'tcx>) -> DeconstructedPat<'p, 'tcx> {
         let singleton = |pat| std::slice::from_ref(self.pattern_arena.alloc(pat));
         let cx = self;
+        let ty = cx.reveal_opaque_ty(pat.ty);
         let ctor;
         let fields: &[_];
         match &pat.kind {
@@ -414,19 +472,22 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
             }
             PatKind::Deref { subpattern } => {
                 fields = singleton(self.lower_pat(subpattern));
-                ctor = match pat.ty.kind() {
+                ctor = match ty.kind() {
                     // This is a box pattern.
                     ty::Adt(adt, ..) if adt.is_box() => Struct,
                     ty::Ref(..) => Ref,
-                    _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
+                    _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
                 };
             }
             PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
-                match pat.ty.kind() {
+                match ty.kind() {
                     ty::Tuple(fs) => {
                         ctor = Struct;
-                        let mut wilds: SmallVec<[_; 2]> =
-                            fs.iter().map(|ty| DeconstructedPat::wildcard(ty)).collect();
+                        let mut wilds: SmallVec<[_; 2]> = fs
+                            .iter()
+                            .map(|ty| cx.reveal_opaque_ty(ty))
+                            .map(|ty| DeconstructedPat::wildcard(ty))
+                            .collect();
                         for pat in subpatterns {
                             wilds[pat.field.index()] = self.lower_pat(&pat.pattern);
                         }
@@ -449,7 +510,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                         let pat = if let Some(pat) = pattern {
                             self.lower_pat(&pat.pattern)
                         } else {
-                            DeconstructedPat::wildcard(args.type_at(0))
+                            DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0)))
                         };
                         ctor = Struct;
                         fields = singleton(pat);
@@ -466,13 +527,12 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                         // For each field in the variant, we store the relevant index into `self.fields` if any.
                         let mut field_id_to_id: Vec<Option<usize>> =
                             (0..variant.fields.len()).map(|_| None).collect();
-                        let tys = cx
-                            .list_variant_nonhidden_fields(pat.ty, variant)
-                            .enumerate()
-                            .map(|(i, (field, ty))| {
+                        let tys = cx.list_variant_nonhidden_fields(ty, variant).enumerate().map(
+                            |(i, (field, ty))| {
                                 field_id_to_id[field.index()] = Some(i);
                                 ty
-                            });
+                            },
+                        );
                         let mut wilds: SmallVec<[_; 2]> =
                             tys.map(|ty| DeconstructedPat::wildcard(ty)).collect();
                         for pat in subpatterns {
@@ -482,11 +542,11 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                         }
                         fields = cx.pattern_arena.alloc_from_iter(wilds);
                     }
-                    _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
+                    _ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
                 }
             }
             PatKind::Constant { value } => {
-                match pat.ty.kind() {
+                match ty.kind() {
                     ty::Bool => {
                         ctor = match value.try_eval_bool(cx.tcx, cx.param_env) {
                             Some(b) => Bool(b),
@@ -497,7 +557,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                     ty::Char | ty::Int(_) | ty::Uint(_) => {
                         ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
                             Some(bits) => {
-                                let x = match *pat.ty.kind() {
+                                let x = match *ty.kind() {
                                     ty::Int(ity) => {
                                         let size = Integer::from_int_ty(&cx.tcx, ity).size().bits();
                                         MaybeInfiniteInt::new_finite_int(bits, size)
@@ -540,7 +600,8 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                         // `Ref`), and has one field. That field has constructor `Str(value)` and no
                         // subfields.
                         // Note: `t` is `str`, not `&str`.
-                        let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat);
+                        let ty = self.reveal_opaque_ty(*t);
+                        let subpattern = DeconstructedPat::new(Str(*value), &[], ty, pat);
                         ctor = Ref;
                         fields = singleton(subpattern)
                     }
@@ -559,7 +620,6 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                     rustc_hir::RangeEnd::Included => RangeEnd::Included,
                     rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
                 };
-                let ty = pat.ty;
                 ctor = match ty.kind() {
                     ty::Char | ty::Int(_) | ty::Uint(_) => {
                         let lo = cx.lower_pat_range_bdy(*lo, ty);
@@ -585,17 +645,17 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                             }
                         }
                     }
-                    _ => bug!("invalid type for range pattern: {}", ty),
+                    _ => bug!("invalid type for range pattern: {}", ty.inner()),
                 };
                 fields = &[];
             }
             PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
-                let array_len = match pat.ty.kind() {
+                let array_len = match ty.kind() {
                     ty::Array(_, length) => {
                         Some(length.eval_target_usize(cx.tcx, cx.param_env) as usize)
                     }
                     ty::Slice(_) => None,
-                    _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", pat.ty),
+                    _ => span_bug!(pat.span, "bad ty {:?} for slice pattern", ty),
                 };
                 let kind = if slice.is_some() {
                     SliceKind::VarLen(prefix.len(), suffix.len())
@@ -624,7 +684,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 fields = &[];
             }
         }
-        DeconstructedPat::new(ctor, fields, pat.ty, pat)
+        DeconstructedPat::new(ctor, fields, ty, pat)
     }
 
     /// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes.
@@ -634,7 +694,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     pub(crate) fn hoist_pat_range_bdy(
         &self,
         miint: MaybeInfiniteInt,
-        ty: Ty<'tcx>,
+        ty: RevealedTy<'tcx>,
     ) -> PatRangeBoundary<'tcx> {
         use MaybeInfiniteInt::*;
         let tcx = self.tcx;
@@ -648,7 +708,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 };
                 match Scalar::try_from_uint(bits, size) {
                     Some(scalar) => {
-                        let value = mir::Const::from_scalar(tcx, scalar, ty);
+                        let value = mir::Const::from_scalar(tcx, scalar, ty.inner());
                         PatRangeBoundary::Finite(value)
                     }
                     // The value doesn't fit. Since `x >= 0` and 0 always encodes the minimum value
@@ -662,7 +722,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
     }
 
     /// Convert back to a `thir::Pat` for diagnostic purposes.
-    pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx> {
+    pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> Pat<'tcx> {
         use MaybeInfiniteInt::*;
         let cx = self;
         let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
@@ -693,10 +753,10 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                 range.hi.minus_one()
             };
             let hi = cx.hoist_pat_range_bdy(hi, ty);
-            PatKind::Range(Box::new(PatRange { lo, hi, end, ty }))
+            PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
         };
 
-        Pat { ty, span: DUMMY_SP, kind }
+        Pat { ty: ty.inner(), span: DUMMY_SP, kind }
     }
     /// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
     /// appear in diagnostics, like float ranges.
@@ -768,7 +828,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
                             }
                         }
                         let suffix: Box<[_]> = subpatterns.collect();
-                        let wild = Pat::wildcard_from_ty(pat.ty());
+                        let wild = Pat::wildcard_from_ty(pat.ty().inner());
                         PatKind::Slice {
                             prefix: prefix.into_boxed_slice(),
                             slice: Some(Box::new(wild)),
@@ -788,7 +848,7 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
             }
         };
 
-        Pat { ty: pat.ty(), span: DUMMY_SP, kind }
+        Pat { ty: pat.ty().inner(), span: DUMMY_SP, kind }
     }
 
     /// Best-effort `Debug` implementation.
@@ -890,7 +950,8 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
 }
 
 impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
-    type Ty = Ty<'tcx>;
+    type Ty = RevealedTy<'tcx>;
+    type Error = ErrorGuaranteed;
     type VariantIdx = VariantIdx;
     type StrLit = Const<'tcx>;
     type ArmData = HirId;
@@ -900,10 +961,6 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
         self.tcx.features().exhaustive_patterns
     }
 
-    fn reveal_opaque_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        self.reveal_opaque_ty(ty)
-    }
-
     fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize {
         self.ctor_arity(ctor, ty)
     }
@@ -914,7 +971,10 @@ impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
     ) -> &[Self::Ty] {
         self.ctor_sub_tys(ctor, ty)
     }
-    fn ctors_for_ty(&self, ty: Self::Ty) -> crate::constructor::ConstructorSet<Self> {
+    fn ctors_for_ty(
+        &self,
+        ty: Self::Ty,
+    ) -> Result<crate::constructor::ConstructorSet<Self>, Self::Error> {
         self.ctors_for_ty(ty)
     }
 
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index d2e621a6b98..b4935d280e6 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -716,8 +716,8 @@ use smallvec::{smallvec, SmallVec};
 use std::fmt;
 
 use crate::constructor::{Constructor, ConstructorSet};
-use crate::pat::{DeconstructedPat, WitnessPat};
-use crate::{Captures, MatchArm, MatchCtxt, TypeCx, TypedArena};
+use crate::pat::{DeconstructedPat, PatOrWild, WitnessPat};
+use crate::{Captures, MatchArm, MatchCtxt, TypeCx};
 
 use self::ValidityConstraint::*;
 
@@ -753,7 +753,7 @@ impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> {
     pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<Cx>) -> &[Cx::Ty] {
         self.mcx.tycx.ctor_sub_tys(ctor, self.ty)
     }
-    pub(crate) fn ctors_for_ty(&self) -> ConstructorSet<Cx> {
+    pub(crate) fn ctors_for_ty(&self) -> Result<ConstructorSet<Cx>, Cx::Error> {
         self.mcx.tycx.ctors_for_ty(self.ty)
     }
 }
@@ -827,7 +827,7 @@ impl fmt::Display for ValidityConstraint {
 #[derivative(Clone(bound = ""))]
 struct PatStack<'p, Cx: TypeCx> {
     // Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
-    pats: SmallVec<[&'p DeconstructedPat<'p, Cx>; 2]>,
+    pats: SmallVec<[PatOrWild<'p, Cx>; 2]>,
     /// Sometimes we know that as far as this row is concerned, the current case is already handled
     /// by a different, more general, case. When the case is irrelevant for all rows this allows us
     /// to skip a case entirely. This is purely an optimization. See at the top for details.
@@ -836,7 +836,7 @@ struct PatStack<'p, Cx: TypeCx> {
 
 impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
     fn from_pattern(pat: &'p DeconstructedPat<'p, Cx>) -> Self {
-        PatStack { pats: smallvec![pat], relevant: true }
+        PatStack { pats: smallvec![PatOrWild::Pat(pat)], relevant: true }
     }
 
     fn is_empty(&self) -> bool {
@@ -847,11 +847,11 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
         self.pats.len()
     }
 
-    fn head(&self) -> &'p DeconstructedPat<'p, Cx> {
+    fn head(&self) -> PatOrWild<'p, Cx> {
         self.pats[0]
     }
 
-    fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
+    fn iter(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Captures<'_> {
         self.pats.iter().copied()
     }
 
@@ -869,13 +869,13 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
     /// Only call if `ctor.is_covered_by(self.head().ctor())` is true.
     fn pop_head_constructor(
         &self,
-        pcx: &PlaceCtxt<'_, 'p, Cx>,
         ctor: &Constructor<Cx>,
+        ctor_arity: usize,
         ctor_is_relevant: bool,
     ) -> PatStack<'p, Cx> {
         // We pop the head pattern and push the new fields extracted from the arguments of
         // `self.head()`.
-        let mut new_pats = self.head().specialize(pcx, ctor);
+        let mut new_pats = self.head().specialize(ctor, ctor_arity);
         new_pats.extend_from_slice(&self.pats[1..]);
         // `ctor` is relevant for this row if it is the actual constructor of this row, or if the
         // row has a wildcard and `ctor` is relevant for wildcards.
@@ -922,11 +922,11 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
         self.pats.len()
     }
 
-    fn head(&self) -> &'p DeconstructedPat<'p, Cx> {
+    fn head(&self) -> PatOrWild<'p, Cx> {
         self.pats.head()
     }
 
-    fn iter(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'_> {
+    fn iter(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Captures<'_> {
         self.pats.iter()
     }
 
@@ -945,13 +945,13 @@ impl<'p, Cx: TypeCx> MatrixRow<'p, Cx> {
     /// Only call if `ctor.is_covered_by(self.head().ctor())` is true.
     fn pop_head_constructor(
         &self,
-        pcx: &PlaceCtxt<'_, 'p, Cx>,
         ctor: &Constructor<Cx>,
+        ctor_arity: usize,
         ctor_is_relevant: bool,
         parent_row: usize,
     ) -> MatrixRow<'p, Cx> {
         MatrixRow {
-            pats: self.pats.pop_head_constructor(pcx, ctor, ctor_is_relevant),
+            pats: self.pats.pop_head_constructor(ctor, ctor_arity, ctor_is_relevant),
             parent_row,
             is_under_guard: self.is_under_guard,
             useful: false,
@@ -981,11 +981,13 @@ struct Matrix<'p, Cx: TypeCx> {
     /// each column must have the same type. Each column corresponds to a place within the
     /// scrutinee.
     rows: Vec<MatrixRow<'p, Cx>>,
-    /// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of
-    /// each column. This must obey the same invariants as the real rows.
-    wildcard_row: PatStack<'p, Cx>,
+    /// Track the type of each column/place.
+    place_ty: SmallVec<[Cx::Ty; 2]>,
     /// Track for each column/place whether it contains a known valid value.
     place_validity: SmallVec<[ValidityConstraint; 2]>,
+    /// Track whether the virtual wildcard row used to compute exhaustiveness is relevant. See top
+    /// of the file for details on relevancy.
+    wildcard_row_is_relevant: bool,
 }
 
 impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
@@ -1004,17 +1006,15 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
 
     /// Build a new matrix from an iterator of `MatchArm`s.
     fn new(
-        wildcard_arena: &'p TypedArena<DeconstructedPat<'p, Cx>>,
         arms: &[MatchArm<'p, Cx>],
         scrut_ty: Cx::Ty,
         scrut_validity: ValidityConstraint,
     ) -> Self {
-        let wild_pattern = wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty));
-        let wildcard_row = PatStack::from_pattern(wild_pattern);
         let mut matrix = Matrix {
             rows: Vec::with_capacity(arms.len()),
-            wildcard_row,
+            place_ty: smallvec![scrut_ty],
             place_validity: smallvec![scrut_validity],
+            wildcard_row_is_relevant: true,
         };
         for (row_id, arm) in arms.iter().enumerate() {
             let v = MatrixRow {
@@ -1028,17 +1028,11 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
         matrix
     }
 
-    fn head_ty(&self, mcx: MatchCtxt<'_, 'p, Cx>) -> Option<Cx::Ty> {
-        if self.column_count() == 0 {
-            return None;
-        }
-
-        let ty = self.wildcard_row.head().ty();
-        // FIXME(Nadrieril): `Cx` should only give us revealed types.
-        Some(mcx.tycx.reveal_opaque_ty(ty))
+    fn head_ty(&self) -> Option<Cx::Ty> {
+        self.place_ty.first().copied()
     }
     fn column_count(&self) -> usize {
-        self.wildcard_row.len()
+        self.place_ty.len()
     }
 
     fn rows(
@@ -1055,7 +1049,7 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
     }
 
     /// Iterate over the first pattern of each row.
-    fn heads(&self) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Clone + Captures<'_> {
+    fn heads(&self) -> impl Iterator<Item = PatOrWild<'p, Cx>> + Clone + Captures<'_> {
         self.rows().map(|r| r.head())
     }
 
@@ -1066,17 +1060,24 @@ impl<'p, Cx: TypeCx> Matrix<'p, Cx> {
         ctor: &Constructor<Cx>,
         ctor_is_relevant: bool,
     ) -> Matrix<'p, Cx> {
-        let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor, ctor_is_relevant);
-        let new_validity = self.place_validity[0].specialize(ctor);
-        let new_place_validity = std::iter::repeat(new_validity)
-            .take(ctor.arity(pcx))
+        let ctor_sub_tys = pcx.ctor_sub_tys(ctor);
+        let arity = ctor_sub_tys.len();
+        let specialized_place_ty =
+            ctor_sub_tys.iter().chain(self.place_ty[1..].iter()).copied().collect();
+        let ctor_sub_validity = self.place_validity[0].specialize(ctor);
+        let specialized_place_validity = std::iter::repeat(ctor_sub_validity)
+            .take(arity)
             .chain(self.place_validity[1..].iter().copied())
             .collect();
-        let mut matrix =
-            Matrix { rows: Vec::new(), wildcard_row, place_validity: new_place_validity };
+        let mut matrix = Matrix {
+            rows: Vec::new(),
+            place_ty: specialized_place_ty,
+            place_validity: specialized_place_validity,
+            wildcard_row_is_relevant: self.wildcard_row_is_relevant && ctor_is_relevant,
+        };
         for (i, row) in self.rows().enumerate() {
             if ctor.is_covered_by(pcx, row.head().ctor()) {
-                let new_row = row.pop_head_constructor(pcx, ctor, ctor_is_relevant, i);
+                let new_row = row.pop_head_constructor(ctor, arity, ctor_is_relevant, i);
                 matrix.expand_and_push(new_row);
             }
         }
@@ -1335,17 +1336,17 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
     mcx: MatchCtxt<'a, 'p, Cx>,
     matrix: &mut Matrix<'p, Cx>,
     is_top_level: bool,
-) -> WitnessMatrix<Cx> {
+) -> Result<WitnessMatrix<Cx>, Cx::Error> {
     debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
 
-    if !matrix.wildcard_row.relevant && matrix.rows().all(|r| !r.pats.relevant) {
+    if !matrix.wildcard_row_is_relevant && matrix.rows().all(|r| !r.pats.relevant) {
         // Here we know that nothing will contribute further to exhaustiveness or usefulness. This
         // is purely an optimization: skipping this check doesn't affect correctness. See the top of
         // the file for details.
-        return WitnessMatrix::empty();
+        return Ok(WitnessMatrix::empty());
     }
 
-    let Some(ty) = matrix.head_ty(mcx) else {
+    let Some(ty) = matrix.head_ty() else {
         // The base case: there are no columns in the matrix. We are morally pattern-matching on ().
         // A row is useful iff it has no (unguarded) rows above it.
         for row in matrix.rows_mut() {
@@ -1354,16 +1355,16 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
             // When there's an unguarded row, the match is exhaustive and any subsequent row is not
             // useful.
             if !row.is_under_guard {
-                return WitnessMatrix::empty();
+                return Ok(WitnessMatrix::empty());
             }
         }
         // No (unguarded) rows, so the match is not exhaustive. We return a new witness unless
         // irrelevant.
-        return if matrix.wildcard_row.relevant {
-            WitnessMatrix::unit_witness()
+        return if matrix.wildcard_row_is_relevant {
+            Ok(WitnessMatrix::unit_witness())
         } else {
             // We choose to not report anything here; see at the top for details.
-            WitnessMatrix::empty()
+            Ok(WitnessMatrix::empty())
         };
     };
 
@@ -1377,7 +1378,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
 
     // Analyze the constructors present in this column.
     let ctors = matrix.heads().map(|p| p.ctor());
-    let ctors_for_ty = pcx.ctors_for_ty();
+    let ctors_for_ty = pcx.ctors_for_ty()?;
     let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
     let split_set = ctors_for_ty.split(pcx, ctors);
     let all_missing = split_set.present.is_empty();
@@ -1416,7 +1417,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
         let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor, ctor_is_relevant);
         let mut witnesses = ensure_sufficient_stack(|| {
             compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false)
-        });
+        })?;
 
         // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
         witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors);
@@ -1437,7 +1438,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
         }
     }
 
-    ret
+    Ok(ret)
 }
 
 /// Indicates whether or not a given arm is useful.
@@ -1468,9 +1469,10 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
     arms: &[MatchArm<'p, Cx>],
     scrut_ty: Cx::Ty,
     scrut_validity: ValidityConstraint,
-) -> UsefulnessReport<'p, Cx> {
-    let mut matrix = Matrix::new(cx.wildcard_arena, arms, scrut_ty, scrut_validity);
-    let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true);
+) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
+    let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
+    let non_exhaustiveness_witnesses =
+        compute_exhaustiveness_and_usefulness(cx, &mut matrix, true)?;
 
     let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
     let arm_usefulness: Vec<_> = arms
@@ -1487,5 +1489,5 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
             (arm, usefulness)
         })
         .collect();
-    UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses }
+    Ok(UsefulnessReport { arm_usefulness, non_exhaustiveness_witnesses })
 }
diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs
index d5883f52819..b5e8ac4018d 100644
--- a/compiler/rustc_query_impl/src/lib.rs
+++ b/compiler/rustc_query_impl/src/lib.rs
@@ -35,7 +35,7 @@ use rustc_middle::ty::TyCtxt;
 use rustc_query_system::dep_graph::SerializedDepNodeIndex;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_query_system::query::{
-    get_query_incr, get_query_non_incr, HashResult, QueryCache, QueryConfig, QueryInfo, QueryMap,
+    get_query_incr, get_query_non_incr, CycleError, HashResult, QueryCache, QueryConfig, QueryMap,
     QueryMode, QueryState,
 };
 use rustc_query_system::HandleCycleError;
@@ -144,10 +144,10 @@ where
     fn value_from_cycle_error(
         self,
         tcx: TyCtxt<'tcx>,
-        cycle: &[QueryInfo],
+        cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value {
-        (self.dynamic.value_from_cycle_error)(tcx, cycle, guar)
+        (self.dynamic.value_from_cycle_error)(tcx, cycle_error, guar)
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs
index f131a0f7593..a827717d9bb 100644
--- a/compiler/rustc_query_impl/src/plumbing.rs
+++ b/compiler/rustc_query_impl/src/plumbing.rs
@@ -69,7 +69,7 @@ impl QueryContext for QueryCtxt<'_> {
     fn next_job_id(self) -> QueryJobId {
         QueryJobId(
             NonZeroU64::new(
-                self.query_system.jobs.fetch_add(1, rustc_data_structures::sync::Ordering::Relaxed),
+                self.query_system.jobs.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
             )
             .unwrap(),
         )
@@ -342,9 +342,9 @@ pub(crate) fn create_query_frame<
             hasher.finish::<Hash64>()
         })
     };
-    let ty_adt_id = key.ty_adt_id();
+    let ty_def_id = key.ty_def_id();
 
-    QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_adt_id, hash)
+    QueryStackFrame::new(description, span, def_id, def_kind, kind, ty_def_id, hash)
 }
 
 pub(crate) fn encode_query_results<'a, 'tcx, Q>(
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index 9b06823dfba..1f09de0ed70 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -4,7 +4,7 @@ use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerR
 use rustc_data_structures::sharded::{self, Sharded};
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::steal::Steal;
-use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc, Ordering};
+use rustc_data_structures::sync::{AtomicU32, AtomicU64, Lock, Lrc};
 use rustc_data_structures::unord::UnordMap;
 use rustc_index::IndexVec;
 use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
@@ -13,7 +13,7 @@ use std::collections::hash_map::Entry;
 use std::fmt::Debug;
 use std::hash::Hash;
 use std::marker::PhantomData;
-use std::sync::atomic::Ordering::Relaxed;
+use std::sync::atomic::Ordering;
 
 use super::query::DepGraphQuery;
 use super::serialized::{GraphEncoder, SerializedDepGraph, SerializedDepNodeIndex};
@@ -476,7 +476,7 @@ impl<D: Deps> DepGraph<D> {
                 let task_deps = &mut *task_deps;
 
                 if cfg!(debug_assertions) {
-                    data.current.total_read_count.fetch_add(1, Relaxed);
+                    data.current.total_read_count.fetch_add(1, Ordering::Relaxed);
                 }
 
                 // As long as we only have a low number of reads we can avoid doing a hash
@@ -506,7 +506,7 @@ impl<D: Deps> DepGraph<D> {
                         }
                     }
                 } else if cfg!(debug_assertions) {
-                    data.current.total_duplicate_read_count.fetch_add(1, Relaxed);
+                    data.current.total_duplicate_read_count.fetch_add(1, Ordering::Relaxed);
                 }
             })
         }
@@ -976,8 +976,8 @@ impl<D: Deps> DepGraph<D> {
     pub fn print_incremental_info(&self) {
         if let Some(data) = &self.data {
             data.current.encoder.borrow().print_incremental_info(
-                data.current.total_read_count.load(Relaxed),
-                data.current.total_duplicate_read_count.load(Relaxed),
+                data.current.total_read_count.load(Ordering::Relaxed),
+                data.current.total_duplicate_read_count.load(Ordering::Relaxed),
             )
         }
     }
@@ -992,7 +992,7 @@ impl<D: Deps> DepGraph<D> {
 
     pub(crate) fn next_virtual_depnode_index(&self) -> DepNodeIndex {
         debug_assert!(self.data.is_none());
-        let index = self.virtual_dep_node_index.fetch_add(1, Relaxed);
+        let index = self.virtual_dep_node_index.fetch_add(1, Ordering::Relaxed);
         DepNodeIndex::from_u32(index)
     }
 }
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index c025fac2631..958d9fdb52a 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -5,7 +5,7 @@ use crate::error::HandleCycleError;
 use crate::ich::StableHashingContext;
 use crate::query::caches::QueryCache;
 use crate::query::DepNodeIndex;
-use crate::query::{QueryContext, QueryInfo, QueryState};
+use crate::query::{CycleError, QueryContext, QueryState};
 
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_span::ErrorGuaranteed;
@@ -57,7 +57,7 @@ pub trait QueryConfig<Qcx: QueryContext>: Copy {
     fn value_from_cycle_error(
         self,
         tcx: Qcx::DepContext,
-        cycle: &[QueryInfo],
+        cycle_error: &CycleError,
         guar: ErrorGuaranteed,
     ) -> Self::Value;
 
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 982b9ee94da..3ef9de7da74 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -556,7 +556,7 @@ pub fn deadlock(query_map: QueryMap, registry: &rayon_core::Registry) {
 
 #[inline(never)]
 #[cold]
-pub(crate) fn report_cycle<'a>(
+pub fn report_cycle<'a>(
     sess: &'a Session,
     CycleError { usage, cycle: stack }: &CycleError,
 ) -> DiagnosticBuilder<'a> {
diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs
index 96a0c7a033a..9ff04c4e910 100644
--- a/compiler/rustc_query_system/src/query/mod.rs
+++ b/compiler/rustc_query_system/src/query/mod.rs
@@ -4,7 +4,9 @@ pub use self::plumbing::*;
 mod job;
 #[cfg(parallel_compiler)]
 pub use self::job::deadlock;
-pub use self::job::{print_query_stack, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap};
+pub use self::job::{
+    print_query_stack, report_cycle, QueryInfo, QueryJob, QueryJobId, QueryJobInfo, QueryMap,
+};
 
 mod caches;
 pub use self::caches::{
@@ -33,7 +35,8 @@ pub struct QueryStackFrame {
     span: Option<Span>,
     pub def_id: Option<DefId>,
     pub def_kind: Option<DefKind>,
-    pub ty_adt_id: Option<DefId>,
+    /// A def-id that is extracted from a `Ty` in a query key
+    pub ty_def_id: Option<DefId>,
     pub dep_kind: DepKind,
     /// This hash is used to deterministically pick
     /// a query to remove cycles in the parallel compiler.
@@ -49,7 +52,7 @@ impl QueryStackFrame {
         def_id: Option<DefId>,
         def_kind: Option<DefKind>,
         dep_kind: DepKind,
-        ty_adt_id: Option<DefId>,
+        ty_def_id: Option<DefId>,
         _hash: impl FnOnce() -> Hash64,
     ) -> Self {
         Self {
@@ -57,7 +60,7 @@ impl QueryStackFrame {
             span,
             def_id,
             def_kind,
-            ty_adt_id,
+            ty_def_id,
             dep_kind,
             #[cfg(parallel_compiler)]
             hash: _hash(),
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 51842664eeb..3bb2cc5634f 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -124,7 +124,7 @@ fn handle_cycle_error<Q, Qcx>(
     query: Q,
     qcx: Qcx,
     cycle_error: &CycleError,
-    mut error: DiagnosticBuilder<'_>,
+    error: DiagnosticBuilder<'_>,
 ) -> Q::Value
 where
     Q: QueryConfig<Qcx>,
@@ -134,7 +134,7 @@ where
     match query.handle_cycle_error() {
         Error => {
             let guar = error.emit();
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
         Fatal => {
             error.emit();
@@ -143,7 +143,7 @@ where
         }
         DelayBug => {
             let guar = error.delay_as_bug();
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
         Stash => {
             let guar = if let Some(root) = cycle_error.cycle.first()
@@ -154,7 +154,7 @@ where
             } else {
                 error.emit()
             };
-            query.value_from_cycle_error(*qcx.dep_context(), &cycle_error.cycle, guar)
+            query.value_from_cycle_error(*qcx.dep_context(), cycle_error, guar)
         }
     }
 }
@@ -211,7 +211,7 @@ where
 }
 
 #[derive(Clone, Debug)]
-pub(crate) struct CycleError {
+pub struct CycleError {
     /// The query and related span that uses the cycle.
     pub usage: Option<(Span, QueryStackFrame)>,
     pub cycle: Vec<QueryInfo>,
@@ -431,17 +431,14 @@ where
                 // We have an inconsistency. This can happen if one of the two
                 // results is tainted by errors. In this case, delay a bug to
                 // ensure compilation is doomed.
-                qcx.dep_context().sess().dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!(
-                        "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
+                qcx.dep_context().sess().dcx().delayed_bug(format!(
+                    "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
                         computed={:#?}\nfed={:#?}",
-                        query.dep_kind(),
-                        key,
-                        formatter(&result),
-                        formatter(&cached_result),
-                    ),
-                );
+                    query.dep_kind(),
+                    key,
+                    formatter(&result),
+                    formatter(&cached_result),
+                ));
             }
         }
     }
diff --git a/compiler/rustc_query_system/src/values.rs b/compiler/rustc_query_system/src/values.rs
index 4f1c182cdb8..133904f59af 100644
--- a/compiler/rustc_query_system/src/values.rs
+++ b/compiler/rustc_query_system/src/values.rs
@@ -1,20 +1,21 @@
 use rustc_span::ErrorGuaranteed;
 
 use crate::dep_graph::DepContext;
-use crate::query::QueryInfo;
+use crate::query::CycleError;
 
 pub trait Value<Tcx: DepContext>: Sized {
-    fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], guar: ErrorGuaranteed) -> Self;
+    fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, guar: ErrorGuaranteed) -> Self;
 }
 
 impl<Tcx: DepContext, T> Value<Tcx> for T {
-    default fn from_cycle_error(tcx: Tcx, cycle: &[QueryInfo], _guar: ErrorGuaranteed) -> T {
+    default fn from_cycle_error(tcx: Tcx, cycle_error: &CycleError, _guar: ErrorGuaranteed) -> T {
         tcx.sess().dcx().abort_if_errors();
         // Ideally we would use `bug!` here. But bug! is only defined in rustc_middle, and it's
         // non-trivial to define it earlier.
         panic!(
-            "<{} as Value>::from_cycle_error called without errors: {cycle:#?}",
-            std::any::type_name::<T>()
+            "<{} as Value>::from_cycle_error called without errors: {:#?}",
+            std::any::type_name::<T>(),
+            cycle_error.cycle,
         );
     }
 }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 4e1fda5479c..9ccfde5e3c6 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -19,7 +19,7 @@ use rustc_ast::{self as ast, AssocItem, AssocItemKind, MetaItemKind, StmtKind};
 use rustc_ast::{Block, Fn, ForeignItem, ForeignItemKind, Impl, Item, ItemKind, NodeId};
 use rustc_attr as attr;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{struct_span_code_err, Applicability};
 use rustc_expand::expand::AstFragment;
 use rustc_hir::def::{self, *};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
@@ -818,7 +818,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             self.r
                 .dcx()
                 .struct_span_err(item.span, "`extern crate self;` requires renaming")
-                .span_suggestion(
+                .with_span_suggestion(
                     item.span,
                     "rename the `self` crate to be able to import it",
                     "extern crate self as name;",
@@ -999,7 +999,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             let msg = format!("`{name}` is already in scope");
             let note =
                 "macro-expanded `#[macro_use]`s may not shadow existing macros (see RFC 1560)";
-            self.r.dcx().struct_span_err(span, msg).note(note).emit();
+            self.r.dcx().struct_span_err(span, msg).with_note(note).emit();
         }
     }
 
@@ -1010,7 +1010,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
         for attr in &item.attrs {
             if attr.has_name(sym::macro_use) {
                 if self.parent_scope.module.parent.is_some() {
-                    struct_span_err!(
+                    struct_span_code_err!(
                         self.r.dcx(),
                         item.span,
                         E0468,
@@ -1024,7 +1024,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                     }
                 }
                 let ill_formed = |span| {
-                    struct_span_err!(self.r.dcx(), span, E0466, "bad macro import").emit();
+                    struct_span_code_err!(self.r.dcx(), span, E0466, "bad macro import").emit();
                 };
                 match attr.meta() {
                     Some(meta) => match meta.kind {
@@ -1095,8 +1095,13 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                         allow_shadowing,
                     );
                 } else {
-                    struct_span_err!(self.r.dcx(), ident.span, E0469, "imported macro not found")
-                        .emit();
+                    struct_span_code_err!(
+                        self.r.dcx(),
+                        ident.span,
+                        E0469,
+                        "imported macro not found"
+                    )
+                    .emit();
                 }
             }
         }
@@ -1110,10 +1115,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
                 let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`";
                 let mut err = self.r.dcx().struct_span_warn(attr.span, msg);
                 if let ast::AttrStyle::Inner = attr.style {
-                    err.help("try an outer attribute: `#[macro_use]`").emit();
-                } else {
-                    err.emit();
+                    err.help("try an outer attribute: `#[macro_use]`");
                 }
+                err.emit();
             } else if !attr.has_name(sym::macro_use) {
                 continue;
             }
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index 3b1f957c890..0e43a35ce73 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -20,7 +20,7 @@
 //    separate step to be able to collapse the adjacent spans that rustfix
 //    will remove
 //
-//  - `check_crate` finally emits the diagnostics based on the data generated
+//  - `check_unused` finally emits the diagnostics based on the data generated
 //    in the last step
 
 use crate::imports::ImportKind;
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 8743b734926..0d744238eeb 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -6,7 +6,7 @@ use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, report_ambiguity_error, struct_span_err, Applicability, DiagCtxt, Diagnostic,
+    pluralize, report_ambiguity_error, struct_span_code_err, Applicability, DiagCtxt, Diagnostic,
     DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle,
 };
 use rustc_feature::BUILTIN_ATTRIBUTES;
@@ -100,6 +100,8 @@ pub(crate) struct ImportSuggestion {
     pub descr: &'static str,
     pub path: Path,
     pub accessible: bool,
+    // false if the path traverses a foreign `#[doc(hidden)]` item.
+    pub doc_visible: bool,
     pub via_import: bool,
     /// An extra note that should be issued if this item is suggested
     pub note: Option<String>,
@@ -151,7 +153,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     BuiltinLintDiagnostics::AmbiguousGlobImports { diag },
                 );
             } else {
-                let mut err = struct_span_err!(self.dcx(), diag.span, E0659, "{}", &diag.msg);
+                let mut err = struct_span_code_err!(self.dcx(), diag.span, E0659, "{}", &diag.msg);
                 report_ambiguity_error(&mut err, diag);
                 err.emit();
             }
@@ -252,15 +254,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let msg = format!("the name `{name}` is defined multiple times");
 
         let mut err = match (old_binding.is_extern_crate(), new_binding.is_extern_crate()) {
-            (true, true) => struct_span_err!(self.dcx(), span, E0259, "{}", msg),
+            (true, true) => struct_span_code_err!(self.dcx(), span, E0259, "{}", msg),
             (true, _) | (_, true) => match new_binding.is_import() && old_binding.is_import() {
-                true => struct_span_err!(self.dcx(), span, E0254, "{}", msg),
-                false => struct_span_err!(self.dcx(), span, E0260, "{}", msg),
+                true => struct_span_code_err!(self.dcx(), span, E0254, "{}", msg),
+                false => struct_span_code_err!(self.dcx(), span, E0260, "{}", msg),
             },
             _ => match (old_binding.is_import_user_facing(), new_binding.is_import_user_facing()) {
-                (false, false) => struct_span_err!(self.dcx(), span, E0428, "{}", msg),
-                (true, true) => struct_span_err!(self.dcx(), span, E0252, "{}", msg),
-                _ => struct_span_err!(self.dcx(), span, E0255, "{}", msg),
+                (false, false) => struct_span_code_err!(self.dcx(), span, E0428, "{}", msg),
+                (true, true) => struct_span_code_err!(self.dcx(), span, E0252, "{}", msg),
+                _ => struct_span_code_err!(self.dcx(), span, E0255, "{}", msg),
             },
         };
 
@@ -657,7 +659,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 let origin_sp = origin.iter().copied().collect::<Vec<_>>();
 
                 let msp = MultiSpan::from_spans(target_sp.clone());
-                let mut err = struct_span_err!(
+                let mut err = struct_span_code_err!(
                     self.dcx(),
                     msp,
                     E0408,
@@ -786,7 +788,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
             ResolutionError::FailedToResolve { last_segment, label, suggestion, module } => {
                 let mut err =
-                    struct_span_err!(self.dcx(), span, E0433, "failed to resolve: {}", &label);
+                    struct_span_code_err!(self.dcx(), span, E0433, "failed to resolve: {}", &label);
                 err.span_label(span, label);
 
                 if let Some((suggestions, msg, applicability)) = suggestion {
@@ -942,16 +944,15 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 trait_item_span,
                 trait_path,
             } => {
-                let mut err = self.dcx().struct_span_err_with_code(
+                self.dcx().struct_span_err(
                     span,
                     format!(
                         "item `{name}` is an associated {kind}, which doesn't match its trait `{trait_path}`",
                     ),
-                    code,
-                );
-                err.span_label(span, "does not match trait");
-                err.span_label(trait_item_span, "item in trait");
-                err
+                )
+                .with_code(code)
+                .with_span_label(span, "does not match trait")
+                .with_span_label(trait_item_span, "item in trait")
             }
             ResolutionError::TraitImplDuplicate { name, trait_item_span, old_span } => self
                 .dcx()
@@ -1146,10 +1147,16 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     {
         let mut candidates = Vec::new();
         let mut seen_modules = FxHashSet::default();
-        let mut worklist = vec![(start_module, ThinVec::<ast::PathSegment>::new(), true)];
+        let start_did = start_module.def_id();
+        let mut worklist = vec![(
+            start_module,
+            ThinVec::<ast::PathSegment>::new(),
+            true,
+            start_did.is_local() || !self.tcx.is_doc_hidden(start_did),
+        )];
         let mut worklist_via_import = vec![];
 
-        while let Some((in_module, path_segments, accessible)) = match worklist.pop() {
+        while let Some((in_module, path_segments, accessible, doc_visible)) = match worklist.pop() {
             None => worklist_via_import.pop(),
             Some(x) => Some(x),
         } {
@@ -1192,6 +1199,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     }
                 }
 
+                let res = name_binding.res();
+                let did = match res {
+                    Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did),
+                    _ => res.opt_def_id(),
+                };
+                let child_doc_visible = doc_visible
+                    && (did.map_or(true, |did| did.is_local() || !this.tcx.is_doc_hidden(did)));
+
                 // collect results based on the filter function
                 // avoid suggesting anything from the same module in which we are resolving
                 // avoid suggesting anything with a hygienic name
@@ -1200,7 +1215,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     && in_module != parent_scope.module
                     && !ident.span.normalize_to_macros_2_0().from_expansion()
                 {
-                    let res = name_binding.res();
                     if filter_fn(res) {
                         // create the path
                         let mut segms = if lookup_ident.span.at_least_rust_2018() {
@@ -1214,10 +1228,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
                         segms.push(ast::PathSegment::from_ident(ident));
                         let path = Path { span: name_binding.span, segments: segms, tokens: None };
-                        let did = match res {
-                            Res::Def(DefKind::Ctor(..), did) => this.tcx.opt_parent(did),
-                            _ => res.opt_def_id(),
-                        };
 
                         if child_accessible {
                             // Remove invisible match if exists
@@ -1257,6 +1267,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                                 descr: res.descr(),
                                 path,
                                 accessible: child_accessible,
+                                doc_visible: child_doc_visible,
                                 note,
                                 via_import,
                             });
@@ -1277,7 +1288,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         // add the module to the lookup
                         if seen_modules.insert(module.def_id()) {
                             if via_import { &mut worklist_via_import } else { &mut worklist }
-                                .push((module, path_segments, child_accessible));
+                                .push((module, path_segments, child_accessible, child_doc_visible));
                         }
                     }
                 }
@@ -1691,8 +1702,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
         // Print the primary message.
         let descr = get_descr(binding);
-        let mut err =
-            struct_span_err!(self.dcx(), ident.span, E0603, "{} `{}` is private", descr, ident);
+        let mut err = struct_span_code_err!(
+            self.dcx(),
+            ident.span,
+            E0603,
+            "{} `{}` is private",
+            descr,
+            ident
+        );
         err.span_label(ident.span, format!("private {descr}"));
 
         let mut not_publicly_reexported = false;
@@ -2687,8 +2704,26 @@ fn show_candidates(
         Vec::new();
 
     candidates.iter().for_each(|c| {
-        (if c.accessible { &mut accessible_path_strings } else { &mut inaccessible_path_strings })
-            .push((pprust::path_to_string(&c.path), c.descr, c.did, &c.note, c.via_import))
+        if c.accessible {
+            // Don't suggest `#[doc(hidden)]` items from other crates
+            if c.doc_visible {
+                accessible_path_strings.push((
+                    pprust::path_to_string(&c.path),
+                    c.descr,
+                    c.did,
+                    &c.note,
+                    c.via_import,
+                ))
+            }
+        } else {
+            inaccessible_path_strings.push((
+                pprust::path_to_string(&c.path),
+                c.descr,
+                c.did,
+                &c.note,
+                c.via_import,
+            ))
+        }
     });
 
     // we want consistent results across executions, but candidates are produced
@@ -2787,9 +2822,7 @@ fn show_candidates(
             err.help(msg);
         }
         true
-    } else if !matches!(mode, DiagnosticMode::Import) {
-        assert!(!inaccessible_path_strings.is_empty());
-
+    } else if !(inaccessible_path_strings.is_empty() || matches!(mode, DiagnosticMode::Import)) {
         let prefix = if let DiagnosticMode::Pattern = mode {
             "you might have meant to match on "
         } else {
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 97297662200..2ebf4c20562 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -17,7 +17,7 @@ use crate::{NameBinding, NameBindingData, NameBindingKind, PathResult};
 use rustc_ast::NodeId;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::intern::Interned;
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan};
 use rustc_hir::def::{self, DefKind, PartialRes};
 use rustc_middle::metadata::ModChild;
 use rustc_middle::metadata::Reexport;
@@ -686,7 +686,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             .collect::<Vec<_>>();
         let msg = format!("unresolved import{} {}", pluralize!(paths.len()), paths.join(", "),);
 
-        let mut diag = struct_span_err!(self.dcx(), span, E0432, "{}", &msg);
+        let mut diag = struct_span_code_err!(self.dcx(), span, E0432, "{}", &msg);
 
         if let Some((_, UnresolvedImportError { note: Some(note), .. })) = errors.iter().last() {
             diag.note(note.clone());
@@ -1233,7 +1233,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 );
             } else {
                 if ns == TypeNS {
-                    let mut err = if crate_private_reexport {
+                    let err = if crate_private_reexport {
                         self.dcx().create_err(CannotBeReexportedCratePublicNS {
                             span: import.span,
                             ident,
@@ -1371,12 +1371,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         let ImportKind::Glob { id, is_prelude, .. } = import.kind else { unreachable!() };
 
         let ModuleOrUniformRoot::Module(module) = import.imported_module.get().unwrap() else {
-            self.dcx().create_err(CannotGlobImportAllCrates { span: import.span }).emit();
+            self.dcx().emit_err(CannotGlobImportAllCrates { span: import.span });
             return;
         };
 
         if module.is_trait() {
-            self.dcx().create_err(ItemsInTraitsAreNotImportable { span: import.span }).emit();
+            self.dcx().emit_err(ItemsInTraitsAreNotImportable { span: import.span });
             return;
         } else if module == import.parent_scope.module {
             return;
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c3026e52430..4a3c8dfe3d7 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1664,7 +1664,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                     } else {
                         ("`'_` cannot be used here", "`'_` is a reserved lifetime name")
                     };
-                    let mut diag = rustc_errors::struct_span_err!(
+                    let mut diag = rustc_errors::struct_span_code_err!(
                         self.r.dcx(),
                         lifetime.ident.span,
                         E0637,
@@ -1853,7 +1853,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                     LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
                     | LifetimeRibKind::AnonymousWarn(_) => {
                         let sess = self.r.tcx.sess;
-                        let mut err = rustc_errors::struct_span_err!(
+                        let mut err = rustc_errors::struct_span_code_err!(
                             sess.dcx(),
                             path_span,
                             E0726,
@@ -2301,7 +2301,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             let report_error = |this: &Self, ns| {
                 if this.should_report_errs() {
                     let what = if ns == TypeNS { "type parameters" } else { "local variables" };
-                    this.r.dcx().create_err(ImportsCannotReferTo { span: ident.span, what }).emit();
+                    this.r.dcx().emit_err(ImportsCannotReferTo { span: ident.span, what });
                 }
             };
 
@@ -2594,13 +2594,13 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             }
 
             if param.ident.name == kw::UnderscoreLifetime {
-                rustc_errors::struct_span_err!(
+                rustc_errors::struct_span_code_err!(
                     self.r.dcx(),
                     param.ident.span,
                     E0637,
                     "`'_` cannot be used here"
                 )
-                .span_label(param.ident.span, "`'_` is a reserved lifetime name")
+                .with_span_label(param.ident.span, "`'_` is a reserved lifetime name")
                 .emit();
                 // Record lifetime res, so lowering knows there is something fishy.
                 self.record_lifetime_param(param.id, LifetimeRes::Error);
@@ -2608,14 +2608,14 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             }
 
             if param.ident.name == kw::StaticLifetime {
-                rustc_errors::struct_span_err!(
+                rustc_errors::struct_span_code_err!(
                     self.r.dcx(),
                     param.ident.span,
                     E0262,
                     "invalid lifetime parameter name: `{}`",
                     param.ident,
                 )
-                .span_label(param.ident.span, "'static is a reserved lifetime name")
+                .with_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_param(param.id, LifetimeRes::Error);
@@ -3076,7 +3076,16 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
         }
 
         let feed_visibility = |this: &mut Self, def_id| {
-            let vis = this.r.tcx.visibility(def_id).expect_local();
+            let vis = this.r.tcx.visibility(def_id);
+            let vis = if vis.is_visible_locally() {
+                vis.expect_local()
+            } else {
+                this.r.dcx().span_delayed_bug(
+                    span,
+                    "error should be emitted when an unexpected trait item is used",
+                );
+                rustc_middle::ty::Visibility::Public
+            };
             this.r.feed_visibility(this.r.local_def_id(id), vis);
         };
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 9bd58dfe82b..2f476ae6cbc 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -16,7 +16,7 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     MultiSpan, SuggestionStyle,
 };
 use rustc_hir as hir;
@@ -429,8 +429,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         let base_error = self.make_base_error(path, span, source, res);
 
         let code = source.error_code(res.is_some());
-        let mut err =
-            self.r.dcx().struct_span_err_with_code(base_error.span, base_error.msg.clone(), code);
+        let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
+        err.code(code);
 
         self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
         self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
@@ -1383,7 +1383,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 | PathSource::TupleStruct(span, _) => {
                     // We want the main underline to cover the suggested code as well for
                     // cleaner output.
-                    err.set_span(*span);
+                    err.span(*span);
                     *span
                 }
                 _ => span,
@@ -1615,7 +1615,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 let field_spans = match source {
                     // e.g. `if let Enum::TupleVariant(field1, field2) = _`
                     PathSource::TupleStruct(_, pattern_spans) => {
-                        err.set_primary_message(
+                        err.primary_message(
                             "cannot match against a tuple struct which contains private fields",
                         );
 
@@ -1628,7 +1628,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         span: call_span,
                         ..
                     })) => {
-                        err.set_primary_message(
+                        err.primary_message(
                             "cannot initialize a tuple struct which contains private fields",
                         );
                         self.suggest_alternative_construction_methods(
@@ -1755,11 +1755,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             .filter_map(|item| {
                 // Only assoc fns that return `Self`
                 let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder();
-                let ret_ty = fn_sig.output();
-                let ret_ty = self
-                    .r
-                    .tcx
-                    .normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), ret_ty);
+                // Don't normalize the return type, because that can cause cycle errors.
+                let ret_ty = fn_sig.output().skip_binder();
                 let ty::Adt(def, _args) = ret_ty.kind() else {
                     return None;
                 };
@@ -2191,15 +2188,20 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     fn find_module(&mut self, def_id: DefId) -> Option<(Module<'a>, ImportSuggestion)> {
         let mut result = None;
         let mut seen_modules = FxHashSet::default();
-        let mut worklist = vec![(self.r.graph_root, ThinVec::new())];
-
-        while let Some((in_module, path_segments)) = worklist.pop() {
+        let root_did = self.r.graph_root.def_id();
+        let mut worklist = vec![(
+            self.r.graph_root,
+            ThinVec::new(),
+            root_did.is_local() || !self.r.tcx.is_doc_hidden(root_did),
+        )];
+
+        while let Some((in_module, path_segments, doc_visible)) = worklist.pop() {
             // abort if the module is already found
             if result.is_some() {
                 break;
             }
 
-            in_module.for_each_child(self.r, |_, ident, _, name_binding| {
+            in_module.for_each_child(self.r, |r, ident, _, name_binding| {
                 // abort if the module is already found or if name_binding is private external
                 if result.is_some() || !name_binding.vis.is_visible_locally() {
                     return;
@@ -2209,6 +2211,8 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                     let mut path_segments = path_segments.clone();
                     path_segments.push(ast::PathSegment::from_ident(ident));
                     let module_def_id = module.def_id();
+                    let doc_visible = doc_visible
+                        && (module_def_id.is_local() || !r.tcx.is_doc_hidden(module_def_id));
                     if module_def_id == def_id {
                         let path =
                             Path { span: name_binding.span, segments: path_segments, tokens: None };
@@ -2219,6 +2223,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                                 descr: "module",
                                 path,
                                 accessible: true,
+                                doc_visible,
                                 note: None,
                                 via_import: false,
                             },
@@ -2226,7 +2231,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                     } else {
                         // add the module to the lookup
                         if seen_modules.insert(module_def_id) {
-                            worklist.push((module, path_segments));
+                            worklist.push((module, path_segments, doc_visible));
                         }
                     }
                 }
@@ -2598,25 +2603,23 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     ) {
         debug_assert_ne!(lifetime_ref.ident.name, kw::UnderscoreLifetime);
         let mut err = if let Some(outer) = outer_lifetime_ref {
-            let mut err = struct_span_err!(
+            struct_span_code_err!(
                 self.r.dcx(),
                 lifetime_ref.ident.span,
                 E0401,
                 "can't use generic parameters from outer item",
-            );
-            err.span_label(lifetime_ref.ident.span, "use of generic parameter from outer item");
-            err.span_label(outer.span, "lifetime parameter from outer item");
-            err
+            )
+            .with_span_label(lifetime_ref.ident.span, "use of generic parameter from outer item")
+            .with_span_label(outer.span, "lifetime parameter from outer item")
         } else {
-            let mut err = struct_span_err!(
+            struct_span_code_err!(
                 self.r.dcx(),
                 lifetime_ref.ident.span,
                 E0261,
                 "use of undeclared lifetime name `{}`",
                 lifetime_ref.ident
-            );
-            err.span_label(lifetime_ref.ident.span, "undeclared lifetime");
-            err
+            )
+            .with_span_label(lifetime_ref.ident.span, "undeclared lifetime")
         };
         self.suggest_introducing_lifetime(
             &mut err,
@@ -2774,7 +2777,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         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!(
+        let mut err = struct_span_code_err!(
             self.r.dcx(),
             spans,
             E0106,
@@ -3279,16 +3282,16 @@ fn mk_where_bound_predicate(
 
 /// Report lifetime/lifetime shadowing as an error.
 pub(super) fn signal_lifetime_shadowing(sess: &Session, orig: Ident, shadower: Ident) {
-    let mut err = struct_span_err!(
+    struct_span_code_err!(
         sess.dcx(),
         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();
+    )
+    .with_span_label(orig.span, "first declared here")
+    .with_span_label(shadower.span, format!("lifetime `{}` already in scope", orig.name))
+    .emit();
 }
 
 struct LifetimeFinder<'ast> {
@@ -3314,11 +3317,12 @@ impl<'ast> Visitor<'ast> for LifetimeFinder<'ast> {
 pub(super) fn signal_label_shadowing(sess: &Session, orig: Span, shadower: Ident) {
     let name = shadower.name;
     let shadower = shadower.span;
-    let mut err = sess.dcx().struct_span_warn(
-        shadower,
-        format!("label name `{name}` shadows a label name that is already in scope"),
-    );
-    err.span_label(orig, "first declared here");
-    err.span_label(shadower, format!("label `{name}` already in scope"));
-    err.emit();
+    sess.dcx()
+        .struct_span_warn(
+            shadower,
+            format!("label name `{name}` shadows a label name that is already in scope"),
+        )
+        .with_span_label(orig, "first declared here")
+        .with_span_label(shadower, format!("label `{name}` already in scope"))
+        .emit();
 }
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 0f7294cdad0..66ecaeb4449 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{struct_span_code_err, Applicability};
 use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand};
 use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
 use rustc_expand::compile_declarative_macro;
@@ -128,7 +128,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
                         let msg = format!("{} `{}` was already registered", "tool", ident);
                         tcx.dcx()
                             .struct_span_err(ident.span, msg)
-                            .span_label(old_ident.span, "already registered here")
+                            .with_span_label(old_ident.span, "already registered here")
                             .emit();
                     }
                 }
@@ -137,7 +137,7 @@ pub(crate) fn registered_tools(tcx: TyCtxt<'_>, (): ()) -> RegisteredTools {
                     let span = nested_meta.span();
                     tcx.dcx()
                         .struct_span_err(span, msg)
-                        .span_label(span, "not an identifier")
+                        .with_span_label(span, "not an identifier")
                         .emit();
                 }
             }
@@ -576,10 +576,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str });
             }
 
-            let mut err = self.dcx().create_err(err);
-            err.span_label(path.span, format!("not {article} {expected}"));
-
-            err.emit();
+            self.dcx()
+                .create_err(err)
+                .with_span_label(path.span, format!("not {article} {expected}"))
+                .emit();
 
             return Ok((self.dummy_ext(kind), Res::Err));
         }
@@ -830,7 +830,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         expected,
                         ident,
                     });
-
                     self.unresolved_macro_suggestions(&mut err, kind, &parent_scope, ident, krate);
                     err.emit();
                 }
@@ -949,13 +948,13 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                         rule_spans = Vec::new();
                     }
                     BuiltinMacroState::AlreadySeen(span) => {
-                        struct_span_err!(
+                        struct_span_code_err!(
                             self.dcx(),
                             item.span,
                             E0773,
                             "attempted to define built-in macro more than once"
                         )
-                        .span_note(span, "previously defined here")
+                        .with_span_note(span, "previously defined here")
                         .emit();
                     }
                 }
diff --git a/compiler/rustc_serialize/tests/opaque.rs b/compiler/rustc_serialize/tests/opaque.rs
index 861091688bb..45ff85f38d2 100644
--- a/compiler/rustc_serialize/tests/opaque.rs
+++ b/compiler/rustc_serialize/tests/opaque.rs
@@ -1,12 +1,12 @@
 #![allow(rustc::internal)]
 
-use rustc_macros::{Decodable, Encodable};
-use rustc_serialize::opaque::{MemDecoder, FileEncoder};
+use rustc_macros::{Decodable_Generic, Encodable_Generic};
+use rustc_serialize::opaque::{FileEncoder, MemDecoder};
 use rustc_serialize::{Decodable, Encodable};
 use std::fmt::Debug;
 use std::fs;
 
-#[derive(PartialEq, Clone, Debug, Encodable, Decodable)]
+#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)]
 struct Struct {
     a: (),
     b: u8,
@@ -209,7 +209,7 @@ fn test_struct() {
     }]);
 }
 
-#[derive(PartialEq, Clone, Debug, Encodable, Decodable)]
+#[derive(PartialEq, Clone, Debug, Encodable_Generic, Decodable_Generic)]
 enum Enum {
     Variant1,
     Variant2(usize, u32),
@@ -258,7 +258,7 @@ fn test_tuples() {
 
 #[test]
 fn test_unit_like_struct() {
-    #[derive(Encodable, Decodable, PartialEq, Debug)]
+    #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)]
     struct UnitLikeStruct;
 
     check_round_trip(vec![UnitLikeStruct]);
@@ -266,7 +266,7 @@ fn test_unit_like_struct() {
 
 #[test]
 fn test_box() {
-    #[derive(Encodable, Decodable, PartialEq, Debug)]
+    #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)]
     struct A {
         foo: Box<[bool]>,
     }
@@ -279,12 +279,12 @@ fn test_box() {
 fn test_cell() {
     use std::cell::{Cell, RefCell};
 
-    #[derive(Encodable, Decodable, PartialEq, Debug)]
+    #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)]
     struct A {
         baz: isize,
     }
 
-    #[derive(Encodable, Decodable, PartialEq, Debug)]
+    #[derive(Encodable_Generic, Decodable_Generic, PartialEq, Debug)]
     struct B {
         foo: Cell<bool>,
         bar: RefCell<A>,
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 89508c77639..61796d7a6ca 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -125,21 +125,6 @@ pub enum LtoCli {
     Unspecified,
 }
 
-/// The different settings that the `-Z dump_mir_spanview` flag can have. `Statement` generates a
-/// document highlighting each span of every statement (including terminators). `Terminator` and
-/// `Block` highlight a single span per `BasicBlock`: the span of the block's `Terminator`, or a
-/// computed span for the block, representing the entire range, covering the block's terminator and
-/// all of its statements.
-#[derive(Clone, Copy, PartialEq, Hash, Debug)]
-pub enum MirSpanview {
-    /// Default `-Z dump_mir_spanview` or `-Z dump_mir_spanview=statement`
-    Statement,
-    /// `-Z dump_mir_spanview=terminator`
-    Terminator,
-    /// `-Z dump_mir_spanview=block`
-    Block,
-}
-
 /// The different settings that the `-C instrument-coverage` flag can have.
 ///
 /// Coverage instrumentation now supports combining `-C instrument-coverage`
@@ -1161,8 +1146,6 @@ impl UnstableOptions {
         DiagCtxtFlags {
             can_emit_warnings,
             treat_err_as_bug: self.treat_err_as_bug,
-            dont_buffer_diagnostics: self.dont_buffer_diagnostics,
-            report_delayed_bugs: self.report_delayed_bugs,
             macro_backtrace: self.macro_backtrace,
             deduplicate_diagnostics: self.deduplicate_diagnostics,
             track_diagnostics: self.track_diagnostics,
@@ -2051,23 +2034,14 @@ fn check_error_format_stability(
     early_dcx: &mut EarlyDiagCtxt,
     unstable_opts: &UnstableOptions,
     error_format: ErrorOutputType,
-    json_rendered: HumanReadableErrorType,
 ) {
     if !unstable_opts.unstable_options {
-        if let ErrorOutputType::Json { pretty: true, json_rendered } = error_format {
-            early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::Json {
-                pretty: false,
-                json_rendered,
-            });
+        if let ErrorOutputType::Json { pretty: true, .. } = error_format {
             early_dcx.early_fatal("`--error-format=pretty-json` is unstable");
         }
         if let ErrorOutputType::HumanReadable(HumanReadableErrorType::AnnotateSnippet(_)) =
             error_format
         {
-            early_dcx.abort_if_error_and_set_error_format(ErrorOutputType::Json {
-                pretty: false,
-                json_rendered,
-            });
             early_dcx.early_fatal("`--error-format=human-annotate-rs` is unstable");
         }
     }
@@ -2665,7 +2639,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
     let mut unstable_opts = UnstableOptions::build(early_dcx, matches);
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(early_dcx, matches);
 
-    check_error_format_stability(early_dcx, &unstable_opts, error_format, json_rendered);
+    check_error_format_stability(early_dcx, &unstable_opts, error_format);
 
     if !unstable_opts.unstable_options && json_unused_externs.is_enabled() {
         early_dcx.early_fatal(
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index 0f86773b73f..b672e760feb 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -18,10 +18,9 @@ pub struct FeatureGateError {
 impl<'a> IntoDiagnostic<'a> for FeatureGateError {
     #[track_caller]
     fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a> {
-        let mut diag = DiagnosticBuilder::new(dcx, level, self.explain);
-        diag.set_span(self.span);
-        diag.code(error_code!(E0658));
-        diag
+        DiagnosticBuilder::new(dcx, level, self.explain)
+            .with_span(self.span)
+            .with_code(error_code!(E0658))
     }
 }
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 8274fd05bc0..c97b18ebd66 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -391,7 +391,6 @@ mod desc {
     pub const parse_strip: &str = "either `none`, `debuginfo`, or `symbols`";
     pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
     pub const parse_optimization_fuel: &str = "crate=integer";
-    pub const parse_mir_spanview: &str = "`statement` (default), `terminator`, or `block`";
     pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub const parse_instrument_coverage: &str =
         "`all` (default), `branch`, `except-unused-generics`, `except-unused-functions`, or `off`";
@@ -866,29 +865,6 @@ mod parse {
         }
     }
 
-    pub(crate) fn parse_mir_spanview(slot: &mut Option<MirSpanview>, v: Option<&str>) -> bool {
-        if v.is_some() {
-            let mut bool_arg = None;
-            if parse_opt_bool(&mut bool_arg, v) {
-                *slot = bool_arg.unwrap().then_some(MirSpanview::Statement);
-                return true;
-            }
-        }
-
-        let Some(v) = v else {
-            *slot = Some(MirSpanview::Statement);
-            return true;
-        };
-
-        *slot = Some(match v.trim_end_matches('s') {
-            "statement" | "stmt" => MirSpanview::Statement,
-            "terminator" | "term" => MirSpanview::Terminator,
-            "block" | "basicblock" => MirSpanview::Block,
-            _ => return false,
-        });
-        true
-    }
-
     pub(crate) fn parse_time_passes_format(slot: &mut TimePassesFormat, v: Option<&str>) -> bool {
         match v {
             None => true,
@@ -1577,9 +1553,6 @@ options! {
     dep_info_omit_d_target: bool = (false, parse_bool, [TRACKED],
         "in dep-info output, omit targets for tracking dependencies of the dep-info files \
         themselves (default: no)"),
-    dont_buffer_diagnostics: bool = (false, parse_bool, [UNTRACKED],
-        "emit diagnostics rather than buffering (breaks NLL error downgrading, sorting) \
-        (default: no)"),
     dual_proc_macros: bool = (false, parse_bool, [TRACKED],
         "load proc macros for both target and host, but only link to the target (default: no)"),
     dump_dep_graph: bool = (false, parse_bool, [UNTRACKED],
@@ -1601,11 +1574,6 @@ options! {
         "exclude the pass number when dumping MIR (used in tests) (default: no)"),
     dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED],
         "in addition to `.mir` files, create graphviz `.dot` files (default: no)"),
-    dump_mir_spanview: Option<MirSpanview> = (None, parse_mir_spanview, [UNTRACKED],
-        "in addition to `.mir` files, create `.html` files to view spans for \
-        all `statement`s (including terminators), only `terminator` spans, or \
-        computed `block` spans (one span encompassing a block's terminator and \
-        all statements)."),
     dump_mono_stats: SwitchWithOptPath = (SwitchWithOptPath::Disabled,
         parse_switch_with_opt_path, [UNTRACKED],
         "output statistics about monomorphization collection"),
@@ -1841,8 +1809,6 @@ options! {
     remark_dir: Option<PathBuf> = (None, parse_opt_pathbuf, [UNTRACKED],
         "directory into which to write optimization remarks (if not specified, they will be \
 written to standard error output)"),
-    report_delayed_bugs: bool = (false, parse_bool, [TRACKED],
-        "immediately print bugs registered with `span_delayed_bug` (default: no)"),
     sanitizer: SanitizerSet = (SanitizerSet::empty(), parse_sanitizers, [TRACKED],
         "use a sanitizer"),
     sanitizer_cfi_canonical_jump_tables: Option<bool> = (Some(true), parse_opt_bool, [TRACKED],
@@ -1875,6 +1841,8 @@ written to standard error output)"),
                      query-blocked, incr-cache-load, incr-result-hashing, query-keys, function-args, args, llvm, artifact-sizes"),
     share_generics: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "make the current crate share its generic instantiations"),
+    shell_argfiles: bool = (false, parse_bool, [UNTRACKED],
+        "allow argument files to be specified with POSIX \"shell-style\" argument quoting"),
     show_span: Option<String> = (None, parse_opt_string, [TRACKED],
         "show spans for compiler debugging (expr|pat|ty)"),
     simulate_remapped_rust_src_base: Option<PathBuf> = (None, parse_opt_pathbuf, [TRACKED],
@@ -1919,8 +1887,8 @@ written to standard error output)"),
     #[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],
-        "use the THIR unsafety checker (default: no)"),
+    thir_unsafeck: bool = (true, parse_bool, [TRACKED],
+        "use the THIR unsafety checker (default: yes)"),
     /// We default to 1 here since we want to behave like
     /// a sequential compiler for now. This'll likely be adjusted
     /// in the future. Note that -Zthreads=0 is the way to get
@@ -1991,6 +1959,8 @@ written to standard error output)"),
         "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"),
+    use_sync_unwind: Option<bool> = (None, parse_opt_bool, [TRACKED],
+        "Generate sync unwind tables instead of async unwind tables (default: no)"),
     validate_mir: bool = (false, parse_bool, [UNTRACKED],
         "validate MIR after each transformation"),
     #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")]
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 439fa18b7fa..598178c3c2a 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -143,11 +143,7 @@ pub fn feature_warn_issue(
     // Decorate this as a future-incompatibility lint as in rustc_middle::lint::struct_lint_level
     let lint = UNSTABLE_SYNTAX_PRE_EXPANSION;
     let future_incompatible = lint.future_incompatible.as_ref().unwrap();
-    err.code(DiagnosticId::Lint {
-        name: lint.name_lower(),
-        has_future_breakage: false,
-        is_force_warn: false,
-    });
+    err.code(DiagnosticId::Lint { name: lint.name_lower(), has_future_breakage: false });
     err.warn(lint.desc);
     err.note(format!("for more information, see {}", future_incompatible.reference));
 
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 9ee7625e5bf..210dc9e0145 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -14,11 +14,9 @@ use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::jobserver::{self, Client};
 use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef};
-use rustc_data_structures::sync::{
-    AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread, Ordering::SeqCst,
-};
-use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
-use rustc_errors::emitter::{DynEmitter, EmitterWriter, HumanReadableErrorType};
+use rustc_data_structures::sync::{AtomicU64, DynSend, DynSync, Lock, Lrc, OneThread};
+use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter;
+use rustc_errors::emitter::{DynEmitter, HumanEmitter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
@@ -44,7 +42,7 @@ use std::fmt;
 use std::ops::{Div, Mul};
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
-use std::sync::{atomic::AtomicBool, Arc};
+use std::sync::{atomic::AtomicBool, atomic::Ordering::SeqCst, Arc};
 
 struct OptimizationFuel {
     /// If `-zfuel=crate=n` is specified, initially set to `n`, otherwise `0`.
@@ -265,7 +263,7 @@ impl Session {
         if !unleashed_features.is_empty() {
             let mut must_err = false;
             // Create a diagnostic pointing at where things got unleashed.
-            self.dcx().emit_warning(errors::SkippingConstChecks {
+            self.dcx().emit_warn(errors::SkippingConstChecks {
                 unleashed_features: unleashed_features
                     .iter()
                     .map(|(span, gate)| {
@@ -343,10 +341,7 @@ impl Session {
         if self.dcx().err_count() == old_count {
             Ok(result)
         } else {
-            Err(self.dcx().span_delayed_bug(
-                rustc_span::DUMMY_SP,
-                "`self.err_count()` changed but an error was not emitted",
-            ))
+            Err(self.dcx().delayed_bug("`self.err_count()` changed but an error was not emitted"))
         }
     }
 
@@ -576,7 +571,7 @@ impl Session {
                         // We only call `msg` in case we can actually emit warnings.
                         // Otherwise, this could cause a `good_path_delayed_bug` to
                         // trigger (issue #79546).
-                        self.dcx().emit_warning(errors::OptimisationFuelExhausted { msg: msg() });
+                        self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() });
                     }
                     fuel.out_of_fuel = true;
                 } else if fuel.remaining > 0 {
@@ -1000,7 +995,7 @@ fn default_emitter(
             let (short, color_config) = kind.unzip();
 
             if let HumanReadableErrorType::AnnotateSnippet(_) = kind {
-                let emitter = AnnotateSnippetEmitterWriter::new(
+                let emitter = AnnotateSnippetEmitter::new(
                     Some(source_map),
                     bundle,
                     fallback_bundle,
@@ -1009,7 +1004,7 @@ fn default_emitter(
                 );
                 Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing))
             } else {
-                let emitter = EmitterWriter::stderr(color_config, fallback_bundle)
+                let emitter = HumanEmitter::stderr(color_config, fallback_bundle)
                     .fluent_bundle(bundle)
                     .sm(Some(source_map))
                     .short_message(short)
@@ -1131,7 +1126,7 @@ pub fn build_session(
         match profiler {
             Ok(profiler) => Some(Arc::new(profiler)),
             Err(e) => {
-                dcx.emit_warning(errors::FailedToCreateProfiler { err: e.to_string() });
+                dcx.emit_warn(errors::FailedToCreateProfiler { err: e.to_string() });
                 None
             }
         }
@@ -1274,7 +1269,10 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
     }
 
     // Cannot enable crt-static with sanitizers on Linux
-    if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() {
+    if sess.crt_static(None)
+        && !sess.opts.unstable_opts.sanitizer.is_empty()
+        && !sess.target.is_like_msvc
+    {
         sess.dcx().emit_err(errors::CannotEnableCrtStaticLinux);
     }
 
@@ -1340,7 +1338,7 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
 
     if sess.opts.unstable_opts.stack_protector != StackProtector::None {
         if !sess.target.options.supports_stack_protector {
-            sess.dcx().emit_warning(errors::StackProtectorNotSupportedForTarget {
+            sess.dcx().emit_warn(errors::StackProtectorNotSupportedForTarget {
                 stack_protector: sess.opts.unstable_opts.stack_protector,
                 target_triple: &sess.opts.target_triple,
             });
@@ -1446,7 +1444,7 @@ impl EarlyDiagCtxt {
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_note(&self, msg: impl Into<DiagnosticMessage>) {
-        self.dcx.struct_note(msg).emit()
+        self.dcx.note(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
@@ -1459,13 +1457,13 @@ impl EarlyDiagCtxt {
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
     pub fn early_err(&self, msg: impl Into<DiagnosticMessage>) -> ErrorGuaranteed {
-        self.dcx.struct_err(msg).emit()
+        self.dcx.err(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_fatal(&self, msg: impl Into<DiagnosticMessage>) -> ! {
-        self.dcx.struct_fatal(msg).emit()
+        self.dcx.fatal(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
@@ -1480,7 +1478,7 @@ impl EarlyDiagCtxt {
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
-        self.dcx.struct_warn(msg).emit()
+        self.dcx.warn(msg)
     }
 
     pub fn initialize_checked_jobserver(&self) {
@@ -1488,7 +1486,10 @@ impl EarlyDiagCtxt {
         jobserver::initialize_checked(|err| {
             #[allow(rustc::untranslatable_diagnostic)]
             #[allow(rustc::diagnostic_outside_of_impl)]
-            self.dcx.struct_warn(err).note("the build environment is likely misconfigured").emit()
+            self.dcx
+                .struct_warn(err)
+                .with_note("the build environment is likely misconfigured")
+                .emit()
         });
     }
 }
@@ -1501,7 +1502,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
     let emitter: Box<DynEmitter> = match output {
         config::ErrorOutputType::HumanReadable(kind) => {
             let (short, color_config) = kind.unzip();
-            Box::new(EmitterWriter::stderr(color_config, fallback_bundle).short_message(short))
+            Box::new(HumanEmitter::stderr(color_config, fallback_bundle).short_message(short))
         }
         config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic(
             pretty,
diff --git a/compiler/rustc_smir/src/rustc_internal/internal.rs b/compiler/rustc_smir/src/rustc_internal/internal.rs
index 17162d0de25..5689e8f3b3d 100644
--- a/compiler/rustc_smir/src/rustc_internal/internal.rs
+++ b/compiler/rustc_smir/src/rustc_internal/internal.rs
@@ -17,7 +17,7 @@ use stable_mir::ty::{
     GenericArgKind, GenericArgs, IndexedVal, IntTy, Movability, Region, RigidTy, Span, TermKind,
     TraitRef, Ty, UintTy, VariantDef, VariantIdx,
 };
-use stable_mir::{CrateItem, DefId};
+use stable_mir::{CrateItem, CrateNum, DefId};
 
 use super::RustcInternal;
 
@@ -28,6 +28,13 @@ impl<'tcx> RustcInternal<'tcx> for CrateItem {
     }
 }
 
+impl<'tcx> RustcInternal<'tcx> for CrateNum {
+    type T = rustc_span::def_id::CrateNum;
+    fn internal(&self, _tables: &mut Tables<'tcx>) -> Self::T {
+        rustc_span::def_id::CrateNum::from_usize(*self)
+    }
+}
+
 impl<'tcx> RustcInternal<'tcx> for DefId {
     type T = rustc_span::def_id::DefId;
     fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T {
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index f84c466cc44..fffc454804d 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -25,8 +25,9 @@ use stable_mir::ty::{
     AdtDef, AdtKind, Allocation, ClosureDef, ClosureKind, Const, FieldDef, FnDef, GenericArgs,
     LineInfo, PolyFnSig, RigidTy, Span, Ty, TyKind, VariantDef,
 };
-use stable_mir::{Crate, CrateItem, DefId, Error, Filename, ItemKind, Symbol};
+use stable_mir::{Crate, CrateItem, CrateNum, DefId, Error, Filename, ItemKind, Symbol};
 use std::cell::RefCell;
+use std::iter;
 
 use crate::rustc_internal::{internal, RustcInternal};
 use crate::rustc_smir::builder::BodyBuilder;
@@ -68,9 +69,14 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
 
     fn all_trait_decls(&self) -> stable_mir::TraitDecls {
         let mut tables = self.0.borrow_mut();
+        tables.tcx.all_traits().map(|trait_def_id| tables.trait_def(trait_def_id)).collect()
+    }
+
+    fn trait_decls(&self, crate_num: CrateNum) -> stable_mir::TraitDecls {
+        let mut tables = self.0.borrow_mut();
         tables
             .tcx
-            .traits(LOCAL_CRATE)
+            .traits(crate_num.internal(&mut *tables))
             .iter()
             .map(|trait_def_id| tables.trait_def(*trait_def_id))
             .collect()
@@ -85,9 +91,19 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
 
     fn all_trait_impls(&self) -> stable_mir::ImplTraitDecls {
         let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        iter::once(LOCAL_CRATE)
+            .chain(tables.tcx.crates(()).iter().copied())
+            .flat_map(|cnum| tcx.trait_impls_in_crate(cnum).iter())
+            .map(|impl_def_id| tables.impl_def(*impl_def_id))
+            .collect()
+    }
+
+    fn trait_impls(&self, crate_num: CrateNum) -> stable_mir::ImplTraitDecls {
+        let mut tables = self.0.borrow_mut();
         tables
             .tcx
-            .trait_impls_in_crate(LOCAL_CRATE)
+            .trait_impls_in_crate(crate_num.internal(&mut *tables))
             .iter()
             .map(|impl_def_id| tables.impl_def(*impl_def_id))
             .collect()
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index f0f1d798d44..c0ecbfb9914 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -12,12 +12,11 @@ use crate::rustc_smir::{alloc, Stable, Tables};
 impl<'tcx> Stable<'tcx> for ty::AliasKind {
     type T = stable_mir::ty::AliasKind;
     fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
-        use rustc_middle::ty::AliasKind::*;
         match self {
-            Projection => stable_mir::ty::AliasKind::Projection,
-            Inherent => stable_mir::ty::AliasKind::Inherent,
-            Opaque => stable_mir::ty::AliasKind::Opaque,
-            Weak => stable_mir::ty::AliasKind::Weak,
+            ty::Projection => stable_mir::ty::AliasKind::Projection,
+            ty::Inherent => stable_mir::ty::AliasKind::Inherent,
+            ty::Opaque => stable_mir::ty::AliasKind::Opaque,
+            ty::Weak => stable_mir::ty::AliasKind::Weak,
         }
     }
 }
@@ -34,10 +33,9 @@ impl<'tcx> Stable<'tcx> for ty::DynKind {
     type T = stable_mir::ty::DynKind;
 
     fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
-        use rustc_middle::ty::DynKind;
         match self {
-            DynKind::Dyn => stable_mir::ty::DynKind::Dyn,
-            DynKind::DynStar => stable_mir::ty::DynKind::DynStar,
+            ty::Dyn => stable_mir::ty::DynKind::Dyn,
+            ty::DynStar => stable_mir::ty::DynKind::DynStar,
         }
     }
 }
diff --git a/compiler/rustc_span/src/def_id.rs b/compiler/rustc_span/src/def_id.rs
index e397fab5459..0c811d7dff1 100644
--- a/compiler/rustc_span/src/def_id.rs
+++ b/compiler/rustc_span/src/def_id.rs
@@ -1,11 +1,13 @@
-use crate::{HashStableContext, Symbol};
+use crate::{HashStableContext, SpanDecoder, SpanEncoder, Symbol};
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher, ToStableHashKey};
+use rustc_data_structures::stable_hasher::{
+    Hash64, HashStable, StableHasher, StableOrd, ToStableHashKey,
+};
 use rustc_data_structures::unhash::Unhasher;
 use rustc_data_structures::AtomicRef;
 use rustc_index::Idx;
 use rustc_macros::HashStable_Generic;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
+use rustc_serialize::{Decodable, Encodable};
 use std::fmt;
 use std::hash::{BuildHasherDefault, Hash, Hasher};
 
@@ -46,20 +48,6 @@ impl fmt::Display for CrateNum {
     }
 }
 
-/// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx.
-/// Therefore, make sure to include the context when encode a `CrateNum`.
-impl<E: Encoder> Encodable<E> for CrateNum {
-    default fn encode(&self, s: &mut E) {
-        s.emit_u32(self.as_u32());
-    }
-}
-
-impl<D: Decoder> Decodable<D> for CrateNum {
-    default fn decode(d: &mut D) -> CrateNum {
-        CrateNum::from_u32(d.read_u32())
-    }
-}
-
 /// A `DefPathHash` is a fixed-size representation of a `DefPath` that is
 /// stable across crate and compilation session boundaries. It consists of two
 /// separate 64-bit hashes. The first uniquely identifies the crate this
@@ -132,6 +120,11 @@ impl Default for DefPathHash {
     }
 }
 
+// Safety: `DefPathHash` sort order is not affected (de)serialization.
+unsafe impl StableOrd for DefPathHash {
+    const CAN_USE_UNSTABLE_SORT: bool = true;
+}
+
 /// A [`StableCrateId`] is a 64-bit hash of a crate name, together with all
 /// `-Cmetadata` arguments, and some other data. It is to [`CrateNum`] what [`DefPathHash`] is to
 /// [`DefId`]. It is stable across compilation sessions.
@@ -220,18 +213,6 @@ rustc_index::newtype_index! {
     }
 }
 
-impl<E: Encoder> Encodable<E> for DefIndex {
-    default fn encode(&self, _: &mut E) {
-        panic!("cannot encode `DefIndex` with `{}`", std::any::type_name::<E>());
-    }
-}
-
-impl<D: Decoder> Decodable<D> for DefIndex {
-    default fn decode(_: &mut D) -> DefIndex {
-        panic!("cannot decode `DefIndex` with `{}`", std::any::type_name::<D>());
-    }
-}
-
 /// A `DefId` identifies a particular *definition*, by combining a crate
 /// index and a def index.
 ///
@@ -347,19 +328,6 @@ impl From<LocalDefId> for DefId {
     }
 }
 
-impl<E: Encoder> Encodable<E> for DefId {
-    default fn encode(&self, s: &mut E) {
-        self.krate.encode(s);
-        self.index.encode(s);
-    }
-}
-
-impl<D: Decoder> Decodable<D> for DefId {
-    default fn decode(d: &mut D) -> DefId {
-        DefId { krate: Decodable::decode(d), index: Decodable::decode(d) }
-    }
-}
-
 pub fn default_def_id_debug(def_id: DefId, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     f.debug_struct("DefId").field("krate", &def_id.krate).field("index", &def_id.index).finish()
 }
@@ -423,13 +391,13 @@ impl fmt::Debug for LocalDefId {
     }
 }
 
-impl<E: Encoder> Encodable<E> for LocalDefId {
+impl<E: SpanEncoder> Encodable<E> for LocalDefId {
     fn encode(&self, s: &mut E) {
         self.to_def_id().encode(s);
     }
 }
 
-impl<D: Decoder> Decodable<D> for LocalDefId {
+impl<D: SpanDecoder> Decodable<D> for LocalDefId {
     fn decode(d: &mut D) -> LocalDefId {
         DefId::decode(d).expect_local()
     }
@@ -490,6 +458,15 @@ impl<CTX: HashStableContext> ToStableHashKey<CTX> for CrateNum {
     }
 }
 
+impl<CTX: HashStableContext> ToStableHashKey<CTX> for DefPathHash {
+    type KeyType = DefPathHash;
+
+    #[inline]
+    fn to_stable_hash_key(&self, _: &CTX) -> DefPathHash {
+        *self
+    }
+}
+
 macro_rules! typed_def_id {
     ($Name:ident, $LocalName:ident) => {
         #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Encodable, Decodable, HashStable_Generic)]
diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs
index b717229b68d..d03965b539c 100644
--- a/compiler/rustc_span/src/hygiene.rs
+++ b/compiler/rustc_span/src/hygiene.rs
@@ -27,7 +27,7 @@
 use crate::def_id::{CrateNum, DefId, StableCrateId, CRATE_DEF_ID, LOCAL_CRATE};
 use crate::edition::Edition;
 use crate::symbol::{kw, sym, Symbol};
-use crate::{with_session_globals, HashStableContext, Span, DUMMY_SP};
+use crate::{with_session_globals, HashStableContext, Span, SpanDecoder, SpanEncoder, DUMMY_SP};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{Hash64, HashStable, HashingControls, StableHasher};
@@ -295,11 +295,13 @@ impl ExpnId {
     pub fn expansion_cause(mut self) -> Option<Span> {
         let mut last_macro = None;
         loop {
+            // Fast path to avoid locking.
+            if self == ExpnId::root() {
+                break;
+            }
             let expn_data = self.expn_data();
             // Stop going up the backtrace once include! is encountered
-            if expn_data.is_root()
-                || expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include)
-            {
+            if expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym::include) {
                 break;
             }
             self = expn_data.call_site.ctxt().outer_expn();
@@ -433,7 +435,7 @@ impl HygieneData {
 
     fn marks(&self, mut ctxt: SyntaxContext) -> Vec<(ExpnId, Transparency)> {
         let mut marks = Vec::new();
-        while ctxt != SyntaxContext::root() {
+        while !ctxt.is_root() {
             debug!("marks: getting parent of {:?}", ctxt);
             marks.push(self.outer_mark(ctxt));
             ctxt = self.parent_ctxt(ctxt);
@@ -443,18 +445,46 @@ impl HygieneData {
     }
 
     fn walk_chain(&self, mut span: Span, to: SyntaxContext) -> Span {
+        let orig_span = span;
         debug!("walk_chain({:?}, {:?})", span, to);
         debug!("walk_chain: span ctxt = {:?}", span.ctxt());
-        while span.from_expansion() && span.ctxt() != to {
+        while span.ctxt() != to && span.from_expansion() {
             let outer_expn = self.outer_expn(span.ctxt());
             debug!("walk_chain({:?}): outer_expn={:?}", span, outer_expn);
             let expn_data = self.expn_data(outer_expn);
             debug!("walk_chain({:?}): expn_data={:?}", span, expn_data);
             span = expn_data.call_site;
         }
+        debug!("walk_chain: for span {:?} >>> return span = {:?}", orig_span, span);
         span
     }
 
+    // We need to walk up and update return span if we meet macro instantiation to be collapsed
+    fn walk_chain_collapsed(
+        &self,
+        mut span: Span,
+        to: Span,
+        collapse_debuginfo_enabled: bool,
+    ) -> Span {
+        let orig_span = span;
+        let mut ret_span = span;
+
+        debug!("walk_chain_collapsed({:?}, {:?})", span, to);
+        debug!("walk_chain_collapsed: span ctxt = {:?}", span.ctxt());
+        while !span.eq_ctxt(to) && span.from_expansion() {
+            let outer_expn = self.outer_expn(span.ctxt());
+            debug!("walk_chain_collapsed({:?}): outer_expn={:?}", span, outer_expn);
+            let expn_data = self.expn_data(outer_expn);
+            debug!("walk_chain_collapsed({:?}): expn_data={:?}", span, expn_data);
+            span = expn_data.call_site;
+            if !collapse_debuginfo_enabled || expn_data.collapse_debuginfo {
+                ret_span = span;
+            }
+        }
+        debug!("walk_chain_collapsed: for span {:?} >>> return span = {:?}", orig_span, ret_span);
+        ret_span
+    }
+
     fn adjust(&self, ctxt: &mut SyntaxContext, expn_id: ExpnId) -> Option<ExpnId> {
         let mut scope = None;
         while !self.is_descendant_of(expn_id, self.outer_expn(*ctxt)) {
@@ -571,6 +601,10 @@ pub fn walk_chain(span: Span, to: SyntaxContext) -> Span {
     HygieneData::with(|data| data.walk_chain(span, to))
 }
 
+pub fn walk_chain_collapsed(span: Span, to: Span, collapse_debuginfo_enabled: bool) -> Span {
+    HygieneData::with(|hdata| hdata.walk_chain_collapsed(span, to, collapse_debuginfo_enabled))
+}
+
 pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) {
     // The new contexts that need updating are at the end of the list and have `$crate` as a name.
     let (len, to_update) = HygieneData::with(|data| {
@@ -656,7 +690,7 @@ impl SyntaxContext {
     }
 
     /// Extend a syntax context with a given expansion and transparency.
-    pub(crate) fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
+    pub fn apply_mark(self, expn_id: ExpnId, transparency: Transparency) -> SyntaxContext {
         HygieneData::with(|data| data.apply_mark(self, expn_id, transparency))
     }
 
@@ -850,21 +884,6 @@ impl fmt::Debug for SyntaxContext {
 }
 
 impl Span {
-    /// Creates a fresh expansion with given properties.
-    /// Expansions are normally created by macros, but in some cases expansions are created for
-    /// other compiler-generated code to set per-span properties like allowed unstable features.
-    /// The returned span belongs to the created expansion and has the new properties,
-    /// but its location is inherited from the current span.
-    pub fn fresh_expansion(self, expn_id: LocalExpnId) -> Span {
-        HygieneData::with(|data| {
-            self.with_ctxt(data.apply_mark(
-                self.ctxt(),
-                expn_id.to_expn_id(),
-                Transparency::Transparent,
-            ))
-        })
-    }
-
     /// Reuses the span but adds information like the kind of the desugaring and features that are
     /// allowed inside this span.
     pub fn mark_with_reason(
@@ -879,7 +898,7 @@ impl Span {
             ..ExpnData::default(ExpnKind::Desugaring(reason), self, edition, None, None)
         };
         let expn_id = LocalExpnId::fresh(expn_data, ctx);
-        self.fresh_expansion(expn_id)
+        self.apply_mark(expn_id.to_expn_id(), Transparency::Transparent)
     }
 }
 
@@ -1431,30 +1450,18 @@ fn for_all_expns_in(
     }
 }
 
-impl<E: Encoder> Encodable<E> for LocalExpnId {
+impl<E: SpanEncoder> Encodable<E> for LocalExpnId {
     fn encode(&self, e: &mut E) {
         self.to_expn_id().encode(e);
     }
 }
 
-impl<E: Encoder> Encodable<E> for ExpnId {
-    default fn encode(&self, _: &mut E) {
-        panic!("cannot encode `ExpnId` with `{}`", std::any::type_name::<E>());
-    }
-}
-
-impl<D: Decoder> Decodable<D> for LocalExpnId {
+impl<D: SpanDecoder> Decodable<D> for LocalExpnId {
     fn decode(d: &mut D) -> Self {
         ExpnId::expect_local(ExpnId::decode(d))
     }
 }
 
-impl<D: Decoder> Decodable<D> for ExpnId {
-    default fn decode(_: &mut D) -> Self {
-        panic!("cannot decode `ExpnId` with `{}`", std::any::type_name::<D>());
-    }
-}
-
 pub fn raw_encode_syntax_context<E: Encoder>(
     ctxt: SyntaxContext,
     context: &HygieneEncodeContext,
@@ -1466,18 +1473,6 @@ pub fn raw_encode_syntax_context<E: Encoder>(
     ctxt.0.encode(e);
 }
 
-impl<E: Encoder> Encodable<E> for SyntaxContext {
-    default fn encode(&self, _: &mut E) {
-        panic!("cannot encode `SyntaxContext` with `{}`", std::any::type_name::<E>());
-    }
-}
-
-impl<D: Decoder> Decodable<D> for SyntaxContext {
-    default fn decode(_: &mut D) -> Self {
-        panic!("cannot decode `SyntaxContext` with `{}`", std::any::type_name::<D>());
-    }
-}
-
 /// Updates the `disambiguator` field of the corresponding `ExpnData`
 /// such that the `Fingerprint` of the `ExpnData` does not collide with
 /// any other `ExpnIds`.
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 8f64eed9a87..4235293823c 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -35,6 +35,8 @@
 #![feature(rustdoc_internals)]
 // tidy-alphabetical-end
 
+extern crate self as rustc_span;
+
 #[macro_use]
 extern crate rustc_macros;
 
@@ -43,6 +45,7 @@ extern crate tracing;
 
 use rustc_data_structures::{outline, AtomicRef};
 use rustc_macros::HashStable_Generic;
+use rustc_serialize::opaque::{FileEncoder, MemDecoder};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
 mod caching_source_map_view;
@@ -58,7 +61,7 @@ pub use hygiene::{DesugaringKind, ExpnKind, MacroKind};
 pub use hygiene::{ExpnData, ExpnHash, ExpnId, LocalExpnId, SyntaxContext};
 use rustc_data_structures::stable_hasher::HashingControls;
 pub mod def_id;
-use def_id::{CrateNum, DefId, DefPathHash, LocalDefId, StableCrateId, LOCAL_CRATE};
+use def_id::{CrateNum, DefId, DefIndex, DefPathHash, LocalDefId, StableCrateId, LOCAL_CRATE};
 pub mod edit_distance;
 mod span_encoding;
 pub use span_encoding::{Span, DUMMY_SP};
@@ -541,10 +544,6 @@ impl Span {
         self.data().with_hi(hi)
     }
     #[inline]
-    pub fn eq_ctxt(self, other: Span) -> bool {
-        self.data_untracked().ctxt == other.data_untracked().ctxt
-    }
-    #[inline]
     pub fn with_ctxt(self, ctxt: SyntaxContext) -> Span {
         self.data_untracked().with_ctxt(ctxt)
     }
@@ -565,14 +564,7 @@ impl Span {
     /// Returns `true` if this span comes from any kind of macro, desugaring or inlining.
     #[inline]
     pub fn from_expansion(self) -> bool {
-        self.ctxt() != SyntaxContext::root()
-    }
-
-    /// Returns `true` if `span` originates in a macro's expansion where debuginfo should be
-    /// collapsed.
-    pub fn in_macro_expansion_with_collapse_debuginfo(self) -> bool {
-        let outer_expn = self.ctxt().outer_expn_data();
-        matches!(outer_expn.kind, ExpnKind::Macro(..)) && outer_expn.collapse_debuginfo
+        !self.ctxt().is_root()
     }
 
     /// Returns `true` if `span` originates in a derive-macro's expansion.
@@ -654,15 +646,15 @@ impl Span {
     /// Returns the source span -- this is either the supplied span, or the span for
     /// the macro callsite that expanded to it.
     pub fn source_callsite(self) -> Span {
-        let expn_data = self.ctxt().outer_expn_data();
-        if !expn_data.is_root() { expn_data.call_site.source_callsite() } else { self }
+        let ctxt = self.ctxt();
+        if !ctxt.is_root() { ctxt.outer_expn_data().call_site.source_callsite() } else { self }
     }
 
     /// The `Span` for the tokens in the previous macro expansion from which `self` was generated,
     /// if any.
     pub fn parent_callsite(self) -> Option<Span> {
-        let expn_data = self.ctxt().outer_expn_data();
-        if !expn_data.is_root() { Some(expn_data.call_site) } else { None }
+        let ctxt = self.ctxt();
+        (!ctxt.is_root()).then(|| ctxt.outer_expn_data().call_site)
     }
 
     /// Walk down the expansion ancestors to find a span that's contained within `outer`.
@@ -747,15 +739,14 @@ impl Span {
     /// else returns the `ExpnData` for the macro definition
     /// corresponding to the source callsite.
     pub fn source_callee(self) -> Option<ExpnData> {
-        let expn_data = self.ctxt().outer_expn_data();
-
-        // Create an iterator of call site expansions
-        iter::successors(Some(expn_data), |expn_data| {
-            Some(expn_data.call_site.ctxt().outer_expn_data())
-        })
-        // Find the last expansion which is not root
-        .take_while(|expn_data| !expn_data.is_root())
-        .last()
+        let mut ctxt = self.ctxt();
+        let mut opt_expn_data = None;
+        while !ctxt.is_root() {
+            let expn_data = ctxt.outer_expn_data();
+            ctxt = expn_data.call_site.ctxt();
+            opt_expn_data = Some(expn_data);
+        }
+        opt_expn_data
     }
 
     /// Checks if a span is "internal" to a macro in which `#[unstable]`
@@ -796,11 +787,12 @@ impl Span {
         let mut prev_span = DUMMY_SP;
         iter::from_fn(move || {
             loop {
-                let expn_data = self.ctxt().outer_expn_data();
-                if expn_data.is_root() {
+                let ctxt = self.ctxt();
+                if ctxt.is_root() {
                     return None;
                 }
 
+                let expn_data = ctxt.outer_expn_data();
                 let is_recursive = expn_data.call_site.source_equal(prev_span);
 
                 prev_span = self;
@@ -826,6 +818,39 @@ impl Span {
         )
     }
 
+    /// Prepare two spans to a combine operation like `to` or `between`.
+    /// FIXME: consider using declarative macro metavariable spans for the given spans if they are
+    /// better suitable for combining (#119412).
+    fn prepare_to_combine(
+        a_orig: Span,
+        b_orig: Span,
+    ) -> Result<(SpanData, SpanData, Option<LocalDefId>), Span> {
+        let (a, b) = (a_orig.data(), b_orig.data());
+
+        if a.ctxt != b.ctxt {
+            // Context mismatches usually happen when procedural macros combine spans copied from
+            // the macro input with spans produced by the macro (`Span::*_site`).
+            // In that case we consider the combined span to be produced by the macro and return
+            // the original macro-produced span as the result.
+            // Otherwise we just fall back to returning the first span.
+            // Combining locations typically doesn't make sense in case of context mismatches.
+            // `is_root` here is a fast path optimization.
+            let a_is_callsite = a.ctxt.is_root() || a.ctxt == b.span().source_callsite().ctxt();
+            return Err(if a_is_callsite { b_orig } else { a_orig });
+        }
+
+        let parent = if a.parent == b.parent { a.parent } else { None };
+        Ok((a, b, parent))
+    }
+
+    /// This span, but in a larger context, may switch to the metavariable span if suitable.
+    pub fn with_neighbor(self, neighbor: Span) -> Span {
+        match Span::prepare_to_combine(self, neighbor) {
+            Ok((this, ..)) => Span::new(this.lo, this.hi, this.ctxt, this.parent),
+            Err(_) => self,
+        }
+    }
+
     /// Returns a `Span` that would enclose both `self` and `end`.
     ///
     /// Note that this can also be used to extend the span "backwards":
@@ -837,26 +862,12 @@ impl Span {
     ///     ^^^^^^^^^^^^^^^^^^^^
     /// ```
     pub fn to(self, end: Span) -> Span {
-        let span_data = self.data();
-        let end_data = end.data();
-        // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
-        // Return the macro span on its own to avoid weird diagnostic output. It is preferable to
-        // have an incomplete span than a completely nonsensical one.
-        if span_data.ctxt != end_data.ctxt {
-            if span_data.ctxt.is_root() {
-                return end;
-            } else if end_data.ctxt.is_root() {
-                return self;
+        match Span::prepare_to_combine(self, end) {
+            Ok((from, to, parent)) => {
+                Span::new(cmp::min(from.lo, to.lo), cmp::max(from.hi, to.hi), from.ctxt, parent)
             }
-            // Both spans fall within a macro.
-            // FIXME(estebank): check if it is the *same* macro.
+            Err(fallback) => fallback,
         }
-        Span::new(
-            cmp::min(span_data.lo, end_data.lo),
-            cmp::max(span_data.hi, end_data.hi),
-            if span_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
-            if span_data.parent == end_data.parent { span_data.parent } else { None },
-        )
     }
 
     /// Returns a `Span` between the end of `self` to the beginning of `end`.
@@ -867,14 +878,12 @@ impl Span {
     ///         ^^^^^^^^^^^^^
     /// ```
     pub fn between(self, end: Span) -> Span {
-        let span = self.data();
-        let end = end.data();
-        Span::new(
-            span.hi,
-            end.lo,
-            if end.ctxt.is_root() { end.ctxt } else { span.ctxt },
-            if span.parent == end.parent { span.parent } else { None },
-        )
+        match Span::prepare_to_combine(self, end) {
+            Ok((from, to, parent)) => {
+                Span::new(cmp::min(from.hi, to.hi), cmp::max(from.lo, to.lo), from.ctxt, parent)
+            }
+            Err(fallback) => fallback,
+        }
     }
 
     /// Returns a `Span` from the beginning of `self` until the beginning of `end`.
@@ -885,31 +894,12 @@ impl Span {
     ///     ^^^^^^^^^^^^^^^^^
     /// ```
     pub fn until(self, end: Span) -> Span {
-        // Most of this function's body is copied from `to`.
-        // We can't just do `self.to(end.shrink_to_lo())`,
-        // because to also does some magic where it uses min/max so
-        // it can handle overlapping spans. Some advanced mis-use of
-        // `until` with different ctxts makes this visible.
-        let span_data = self.data();
-        let end_data = end.data();
-        // FIXME(jseyfried): `self.ctxt` should always equal `end.ctxt` here (cf. issue #23480).
-        // Return the macro span on its own to avoid weird diagnostic output. It is preferable to
-        // have an incomplete span than a completely nonsensical one.
-        if span_data.ctxt != end_data.ctxt {
-            if span_data.ctxt.is_root() {
-                return end;
-            } else if end_data.ctxt.is_root() {
-                return self;
+        match Span::prepare_to_combine(self, end) {
+            Ok((from, to, parent)) => {
+                Span::new(cmp::min(from.lo, to.lo), cmp::max(from.lo, to.lo), from.ctxt, parent)
             }
-            // Both spans fall within a macro.
-            // FIXME(estebank): check if it is the *same* macro.
+            Err(fallback) => fallback,
         }
-        Span::new(
-            span_data.lo,
-            end_data.lo,
-            if end_data.ctxt.is_root() { end_data.ctxt } else { span_data.ctxt },
-            if span_data.parent == end_data.parent { span_data.parent } else { None },
-        )
     }
 
     pub fn from_inner(self, inner: InnerSpan) -> Span {
@@ -1016,20 +1006,203 @@ impl Default for Span {
     }
 }
 
-impl<E: Encoder> Encodable<E> for Span {
-    default fn encode(&self, s: &mut E) {
-        let span = self.data();
-        span.lo.encode(s);
-        span.hi.encode(s);
+rustc_index::newtype_index! {
+    #[orderable]
+    #[debug_format = "AttrId({})"]
+    pub struct AttrId {}
+}
+
+/// This trait is used to allow encoder specific encodings of certain types.
+/// It is similar to rustc_type_ir's TyEncoder.
+pub trait SpanEncoder: Encoder {
+    fn encode_span(&mut self, span: Span);
+    fn encode_symbol(&mut self, symbol: Symbol);
+    fn encode_expn_id(&mut self, expn_id: ExpnId);
+    fn encode_syntax_context(&mut self, syntax_context: SyntaxContext);
+    /// As a local identifier, a `CrateNum` is only meaningful within its context, e.g. within a tcx.
+    /// Therefore, make sure to include the context when encode a `CrateNum`.
+    fn encode_crate_num(&mut self, crate_num: CrateNum);
+    fn encode_def_index(&mut self, def_index: DefIndex);
+    fn encode_def_id(&mut self, def_id: DefId);
+}
+
+impl SpanEncoder for FileEncoder {
+    fn encode_span(&mut self, span: Span) {
+        let span = span.data();
+        span.lo.encode(self);
+        span.hi.encode(self);
+    }
+
+    fn encode_symbol(&mut self, symbol: Symbol) {
+        self.emit_str(symbol.as_str());
+    }
+
+    fn encode_expn_id(&mut self, _expn_id: ExpnId) {
+        panic!("cannot encode `ExpnId` with `FileEncoder`");
+    }
+
+    fn encode_syntax_context(&mut self, _syntax_context: SyntaxContext) {
+        panic!("cannot encode `SyntaxContext` with `FileEncoder`");
+    }
+
+    fn encode_crate_num(&mut self, crate_num: CrateNum) {
+        self.emit_u32(crate_num.as_u32());
+    }
+
+    fn encode_def_index(&mut self, _def_index: DefIndex) {
+        panic!("cannot encode `DefIndex` with `FileEncoder`");
+    }
+
+    fn encode_def_id(&mut self, def_id: DefId) {
+        def_id.krate.encode(self);
+        def_id.index.encode(self);
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for Span {
+    fn encode(&self, s: &mut E) {
+        s.encode_span(*self);
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for Symbol {
+    fn encode(&self, s: &mut E) {
+        s.encode_symbol(*self);
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for ExpnId {
+    fn encode(&self, s: &mut E) {
+        s.encode_expn_id(*self)
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for SyntaxContext {
+    fn encode(&self, s: &mut E) {
+        s.encode_syntax_context(*self)
     }
 }
-impl<D: Decoder> Decodable<D> for Span {
-    default fn decode(s: &mut D) -> Span {
-        let lo = Decodable::decode(s);
-        let hi = Decodable::decode(s);
+
+impl<E: SpanEncoder> Encodable<E> for CrateNum {
+    fn encode(&self, s: &mut E) {
+        s.encode_crate_num(*self)
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for DefIndex {
+    fn encode(&self, s: &mut E) {
+        s.encode_def_index(*self)
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for DefId {
+    fn encode(&self, s: &mut E) {
+        s.encode_def_id(*self)
+    }
+}
+
+impl<E: SpanEncoder> Encodable<E> for AttrId {
+    fn encode(&self, _s: &mut E) {
+        // A fresh id will be generated when decoding
+    }
+}
+
+/// This trait is used to allow decoder specific encodings of certain types.
+/// It is similar to rustc_type_ir's TyDecoder.
+pub trait SpanDecoder: Decoder {
+    fn decode_span(&mut self) -> Span;
+    fn decode_symbol(&mut self) -> Symbol;
+    fn decode_expn_id(&mut self) -> ExpnId;
+    fn decode_syntax_context(&mut self) -> SyntaxContext;
+    fn decode_crate_num(&mut self) -> CrateNum;
+    fn decode_def_index(&mut self) -> DefIndex;
+    fn decode_def_id(&mut self) -> DefId;
+    fn decode_attr_id(&mut self) -> AttrId;
+}
+
+impl SpanDecoder for MemDecoder<'_> {
+    fn decode_span(&mut self) -> Span {
+        let lo = Decodable::decode(self);
+        let hi = Decodable::decode(self);
 
         Span::new(lo, hi, SyntaxContext::root(), None)
     }
+
+    fn decode_symbol(&mut self) -> Symbol {
+        Symbol::intern(self.read_str())
+    }
+
+    fn decode_expn_id(&mut self) -> ExpnId {
+        panic!("cannot decode `ExpnId` with `MemDecoder`");
+    }
+
+    fn decode_syntax_context(&mut self) -> SyntaxContext {
+        panic!("cannot decode `SyntaxContext` with `MemDecoder`");
+    }
+
+    fn decode_crate_num(&mut self) -> CrateNum {
+        CrateNum::from_u32(self.read_u32())
+    }
+
+    fn decode_def_index(&mut self) -> DefIndex {
+        panic!("cannot decode `DefIndex` with `MemDecoder`");
+    }
+
+    fn decode_def_id(&mut self) -> DefId {
+        DefId { krate: Decodable::decode(self), index: Decodable::decode(self) }
+    }
+
+    fn decode_attr_id(&mut self) -> AttrId {
+        panic!("cannot decode `AttrId` with `MemDecoder`");
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for Span {
+    fn decode(s: &mut D) -> Span {
+        s.decode_span()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for Symbol {
+    fn decode(s: &mut D) -> Symbol {
+        s.decode_symbol()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for ExpnId {
+    fn decode(s: &mut D) -> ExpnId {
+        s.decode_expn_id()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for SyntaxContext {
+    fn decode(s: &mut D) -> SyntaxContext {
+        s.decode_syntax_context()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for CrateNum {
+    fn decode(s: &mut D) -> CrateNum {
+        s.decode_crate_num()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for DefIndex {
+    fn decode(s: &mut D) -> DefIndex {
+        s.decode_def_index()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for DefId {
+    fn decode(s: &mut D) -> DefId {
+        s.decode_def_id()
+    }
+}
+
+impl<D: SpanDecoder> Decodable<D> for AttrId {
+    fn decode(s: &mut D) -> AttrId {
+        s.decode_attr_id()
+    }
 }
 
 /// Insert `source_map` into the session globals for the duration of the
@@ -1360,7 +1533,7 @@ impl Clone for SourceFile {
     }
 }
 
-impl<S: Encoder> Encodable<S> for SourceFile {
+impl<S: SpanEncoder> Encodable<S> for SourceFile {
     fn encode(&self, s: &mut S) {
         self.name.encode(s);
         self.src_hash.encode(s);
@@ -1434,7 +1607,7 @@ impl<S: Encoder> Encodable<S> for SourceFile {
     }
 }
 
-impl<D: Decoder> Decodable<D> for SourceFile {
+impl<D: SpanDecoder> Decodable<D> for SourceFile {
     fn decode(d: &mut D) -> SourceFile {
         let name: FileName = Decodable::decode(d);
         let src_hash: SourceFileHash = Decodable::decode(d);
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index c61dbcaae95..8253ffefcaa 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -23,9 +23,15 @@ mod tests;
 /// otherwise return the call site span up to the `enclosing_sp` by
 /// following the `expn_data` chain.
 pub fn original_sp(sp: Span, enclosing_sp: Span) -> Span {
-    let expn_data1 = sp.ctxt().outer_expn_data();
-    let expn_data2 = enclosing_sp.ctxt().outer_expn_data();
-    if expn_data1.is_root() || !expn_data2.is_root() && expn_data1.call_site == expn_data2.call_site
+    let ctxt = sp.ctxt();
+    if ctxt.is_root() {
+        return sp;
+    }
+
+    let enclosing_ctxt = enclosing_sp.ctxt();
+    let expn_data1 = ctxt.outer_expn_data();
+    if !enclosing_ctxt.is_root()
+        && expn_data1.call_site == enclosing_ctxt.outer_expn_data().call_site
     {
         sp
     } else {
diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs
index 130522a302d..5788d11ed43 100644
--- a/compiler/rustc_span/src/source_map/tests.rs
+++ b/compiler/rustc_span/src/source_map/tests.rs
@@ -18,7 +18,7 @@ impl SourceMap {
     ///    * the LHS span must start at or before the RHS span.
     fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span> {
         // Ensure we're at the same expansion ID.
-        if sp_lhs.ctxt() != sp_rhs.ctxt() {
+        if !sp_lhs.eq_ctxt(sp_rhs) {
             return None;
         }
 
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index f7d17a267d6..e162695a13b 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -210,12 +210,10 @@ impl Span {
         }
     }
 
-    /// This function is used as a fast path when decoding the full `SpanData` is not necessary.
-    /// It's a cut-down version of `data_untracked`.
-    #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")]
-    #[inline]
-    pub fn ctxt(self) -> SyntaxContext {
-        if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
+    // Returns either syntactic context, if it can be retrieved without taking the interner lock,
+    // or an index into the interner if it cannot.
+    fn inline_ctxt(self) -> Result<SyntaxContext, usize> {
+        Ok(if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
             if self.len_with_tag_or_marker & PARENT_TAG == 0 {
                 // Inline-context format.
                 SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
@@ -223,17 +221,36 @@ impl Span {
                 // Inline-parent format. We know that the SyntaxContext is root.
                 SyntaxContext::root()
             }
+        } else if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
+            // Partially-interned format. This path avoids looking up the
+            // interned value, and is the whole point of the
+            // partially-interned format.
+            SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
         } else {
-            if self.ctxt_or_parent_or_marker != CTXT_INTERNED_MARKER {
-                // Partially-interned format. This path avoids looking up the
-                // interned value, and is the whole point of the
-                // partially-interned format.
-                SyntaxContext::from_u32(self.ctxt_or_parent_or_marker as u32)
-            } else {
-                // Fully-interned format.
-                let index = self.lo_or_index;
-                with_span_interner(|interner| interner.spans[index as usize].ctxt)
+            // Fully-interned format.
+            return Err(self.lo_or_index as usize);
+        })
+    }
+
+    /// This function is used as a fast path when decoding the full `SpanData` is not necessary.
+    /// It's a cut-down version of `data_untracked`.
+    #[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")]
+    #[inline]
+    pub fn ctxt(self) -> SyntaxContext {
+        self.inline_ctxt()
+            .unwrap_or_else(|index| with_span_interner(|interner| interner.spans[index].ctxt))
+    }
+
+    #[inline]
+    pub fn eq_ctxt(self, other: Span) -> bool {
+        match (self.inline_ctxt(), other.inline_ctxt()) {
+            (Ok(ctxt1), Ok(ctxt2)) => ctxt1 == ctxt2,
+            (Ok(ctxt), Err(index)) | (Err(index), Ok(ctxt)) => {
+                with_span_interner(|interner| ctxt == interner.spans[index].ctxt)
             }
+            (Err(index1), Err(index2)) => with_span_interner(|interner| {
+                interner.spans[index1].ctxt == interner.spans[index2].ctxt
+            }),
         }
     }
 }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 0b44071496e..8ed1255c010 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -4,10 +4,11 @@
 
 use rustc_arena::DroplessArena;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
+use rustc_data_structures::stable_hasher::{
+    HashStable, StableCompare, StableHasher, ToStableHashKey,
+};
 use rustc_data_structures::sync::Lock;
 use rustc_macros::HashStable_Generic;
-use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
 
 use std::fmt;
 use std::hash::{Hash, Hasher};
@@ -1041,6 +1042,7 @@ symbols! {
         mir_offset,
         mir_retag,
         mir_return,
+        mir_return_to,
         mir_set_discriminant,
         mir_static,
         mir_static_mut,
@@ -2075,19 +2077,6 @@ impl ToString for Symbol {
     }
 }
 
-impl<S: Encoder> Encodable<S> for Symbol {
-    default fn encode(&self, s: &mut S) {
-        s.emit_str(self.as_str());
-    }
-}
-
-impl<D: Decoder> Decodable<D> for Symbol {
-    #[inline]
-    default fn decode(d: &mut D) -> Symbol {
-        Symbol::intern(d.read_str())
-    }
-}
-
 impl<CTX> HashStable<CTX> for Symbol {
     #[inline]
     fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
@@ -2103,6 +2092,14 @@ impl<CTX> ToStableHashKey<CTX> for Symbol {
     }
 }
 
+impl StableCompare for Symbol {
+    const CAN_USE_UNSTABLE_SORT: bool = true;
+
+    fn stable_cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
 pub(crate) struct Interner(Lock<InternerInner>);
 
 // The `&'static str`s in this type actually point into the arena.
diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs
index 06a2b3ca9c4..2d076f7b225 100644
--- a/compiler/rustc_symbol_mangling/src/errors.rs
+++ b/compiler/rustc_symbol_mangling/src/errors.rs
@@ -18,9 +18,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for TestOutput {
         let TestOutput { span, kind, content } = self;
 
         #[allow(rustc::untranslatable_diagnostic)]
-        let mut diag = DiagnosticBuilder::new(dcx, level, format!("{kind}({content})"));
-        diag.set_span(span);
-        diag
+        DiagnosticBuilder::new(dcx, level, format!("{kind}({content})")).with_span(span)
     }
 }
 
diff --git a/compiler/rustc_target/src/abi/mod.rs b/compiler/rustc_target/src/abi/mod.rs
index a274790bffc..24e49ff648f 100644
--- a/compiler/rustc_target/src/abi/mod.rs
+++ b/compiler/rustc_target/src/abi/mod.rs
@@ -120,11 +120,11 @@ impl<'a> Layout<'a> {
     /// Whether the layout is from a type that implements [`std::marker::PointerLike`].
     ///
     /// Currently, that means that the type is pointer-sized, pointer-aligned,
-    /// and has a scalar ABI.
+    /// and has a initialized (non-union), scalar ABI.
     pub fn is_pointer_like(self, data_layout: &TargetDataLayout) -> bool {
         self.size() == data_layout.pointer_size
             && self.align().abi == data_layout.pointer_align.abi
-            && matches!(self.abi(), Abi::Scalar(..))
+            && matches!(self.abi(), Abi::Scalar(Scalar::Initialized { .. }))
     }
 }
 
diff --git a/compiler/rustc_target/src/asm/s390x.rs b/compiler/rustc_target/src/asm/s390x.rs
index 0a50064f587..b8afeb824d8 100644
--- a/compiler/rustc_target/src/asm/s390x.rs
+++ b/compiler/rustc_target/src/asm/s390x.rs
@@ -6,6 +6,7 @@ use std::fmt;
 def_reg_class! {
     S390x S390xInlineAsmRegClass {
         reg,
+        reg_addr,
         freg,
     }
 }
@@ -36,7 +37,7 @@ impl S390xInlineAsmRegClass {
         arch: InlineAsmArch,
     ) -> &'static [(InlineAsmType, Option<Symbol>)] {
         match (self, arch) {
-            (Self::reg, _) => types! { _: I8, I16, I32, I64; },
+            (Self::reg | Self::reg_addr, _) => types! { _: I8, I16, I32, I64; },
             (Self::freg, _) => types! { _: F32, F64; },
         }
     }
@@ -45,19 +46,19 @@ impl S390xInlineAsmRegClass {
 def_regs! {
     S390x S390xInlineAsmReg S390xInlineAsmRegClass {
         r0: reg = ["r0"],
-        r1: reg = ["r1"],
-        r2: reg = ["r2"],
-        r3: reg = ["r3"],
-        r4: reg = ["r4"],
-        r5: reg = ["r5"],
-        r6: reg = ["r6"],
-        r7: reg = ["r7"],
-        r8: reg = ["r8"],
-        r9: reg = ["r9"],
-        r10: reg = ["r10"],
-        r12: reg = ["r12"],
-        r13: reg = ["r13"],
-        r14: reg = ["r14"],
+        r1: reg, reg_addr = ["r1"],
+        r2: reg, reg_addr = ["r2"],
+        r3: reg, reg_addr = ["r3"],
+        r4: reg, reg_addr = ["r4"],
+        r5: reg, reg_addr = ["r5"],
+        r6: reg, reg_addr = ["r6"],
+        r7: reg, reg_addr = ["r7"],
+        r8: reg, reg_addr = ["r8"],
+        r9: reg, reg_addr = ["r9"],
+        r10: reg, reg_addr = ["r10"],
+        r12: reg, reg_addr = ["r12"],
+        r13: reg, reg_addr = ["r13"],
+        r14: reg, reg_addr = ["r14"],
         f0: freg = ["f0"],
         f1: freg = ["f1"],
         f2: freg = ["f2"],
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 9d25388b90f..8e26327196a 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1601,6 +1601,8 @@ supported_targets! {
     ("riscv32imc-unknown-none-elf", riscv32imc_unknown_none_elf),
     ("riscv32imc-esp-espidf", riscv32imc_esp_espidf),
     ("riscv32imac-esp-espidf", riscv32imac_esp_espidf),
+    ("riscv32imafc-esp-espidf", riscv32imafc_esp_espidf),
+
     ("riscv32imac-unknown-none-elf", riscv32imac_unknown_none_elf),
     ("riscv32imafc-unknown-none-elf", riscv32imafc_unknown_none_elf),
     ("riscv32imac-unknown-xous-elf", riscv32imac_unknown_xous_elf),
diff --git a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs
index ba80c23196e..5abc3017bf8 100644
--- a/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/i686_pc_windows_msvc.rs
@@ -1,9 +1,10 @@
-use crate::spec::{base, LinkerFlavor, Lld, Target};
+use crate::spec::{base, LinkerFlavor, Lld, SanitizerSet, Target};
 
 pub fn target() -> Target {
     let mut base = base::windows_msvc::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
+    base.supported_sanitizers = SanitizerSet::ADDRESS;
 
     base.add_pre_link_args(
         LinkerFlavor::Msvc(Lld::No),
diff --git a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
index 9102673ef77..6884e078c27 100644
--- a/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/i686_unknown_hurd_gnu.rs
@@ -5,7 +5,7 @@ pub fn target() -> Target {
     base.cpu = "pentiumpro".into();
     base.max_atomic_width = Some(64);
     base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m32"]);
-    base.stack_probes = StackProbeType::InlineOrCall { min_llvm_version_for_inline: (11, 0, 1) };
+    base.stack_probes = StackProbeType::Inline;
 
     Target {
         llvm_target: "i686-unknown-hurd-gnu".into(),
diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs
new file mode 100644
index 00000000000..6c7c920bd18
--- /dev/null
+++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_esp_espidf.rs
@@ -0,0 +1,30 @@
+use crate::spec::{cvs, PanicStrategy, RelocModel, Target, TargetOptions};
+
+pub fn target() -> Target {
+    Target {
+        data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(),
+        llvm_target: "riscv32".into(),
+        pointer_width: 32,
+        arch: "riscv32".into(),
+
+        options: TargetOptions {
+            families: cvs!["unix"],
+            os: "espidf".into(),
+            env: "newlib".into(),
+            vendor: "espressif".into(),
+            linker: Some("riscv32-esp-elf-gcc".into()),
+            cpu: "generic-rv32".into(),
+
+            max_atomic_width: Some(32),
+            atomic_cas: true,
+
+            llvm_abiname: "ilp32f".into(),
+            features: "+m,+a,+c,+f".into(),
+            panic_strategy: PanicStrategy::Abort,
+            relocation_model: RelocModel::Static,
+            emit_debug_gdb_scripts: false,
+            eh_frame_header: false,
+            ..Default::default()
+        },
+    }
+}
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs
index 7d6276a0c2d..3a4da91c244 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_pc_windows_msvc.rs
@@ -1,10 +1,11 @@
-use crate::spec::{base, Target};
+use crate::spec::{base, SanitizerSet, Target};
 
 pub fn target() -> Target {
     let mut base = base::windows_msvc::opts();
     base.cpu = "x86-64".into();
     base.plt_by_default = false;
     base.max_atomic_width = Some(64);
+    base.supported_sanitizers = SanitizerSet::ADDRESS;
 
     Target {
         llvm_target: "x86_64-pc-windows-msvc".into(),
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs
index f0515615b19..5b19ed1b5ff 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_fuchsia.rs
@@ -6,7 +6,8 @@ pub fn target() -> Target {
     base.plt_by_default = false;
     base.max_atomic_width = Some(64);
     base.stack_probes = StackProbeType::Inline;
-    base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
+    base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::LEAK;
+    base.supports_xray = true;
 
     Target {
         llvm_target: "x86_64-unknown-fuchsia".into(),
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index bea6fbd6ac5..5eff52afbca 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -66,12 +66,9 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_
     ) -> rustc_errors::DiagnosticBuilder<'_, G> {
         let mut diag =
             DiagnosticBuilder::new(dcx, level, fluent::trait_selection_negative_positive_conflict);
-        diag.set_arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
-        diag.set_arg(
-            "self_desc",
-            self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()),
-        );
-        diag.set_span(self.impl_span);
+        diag.arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
+        diag.arg("self_desc", self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()));
+        diag.span(self.impl_span);
         diag.code(rustc_errors::error_code!(E0751));
         match self.negative_impl_span {
             Ok(span) => {
@@ -79,7 +76,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_
             }
             Err(cname) => {
                 diag.note(fluent::trait_selection_negative_implementation_in_crate);
-                diag.set_arg("negative_impl_cname", cname.to_string());
+                diag.arg("negative_impl_cname", cname.to_string());
             }
         }
         match self.positive_impl_span {
@@ -88,7 +85,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for NegativePositiveConflict<'_
             }
             Err(cname) => {
                 diag.note(fluent::trait_selection_positive_implementation_in_crate);
-                diag.set_arg("positive_impl_cname", cname.to_string());
+                diag.arg("positive_impl_cname", cname.to_string());
             }
         }
         diag
@@ -115,7 +112,7 @@ impl AddToDiagnostic for AdjustSignatureBorrow {
     {
         match self {
             AdjustSignatureBorrow::Borrow { to_borrow } => {
-                diag.set_arg("len", to_borrow.len());
+                diag.arg("len", to_borrow.len());
                 diag.multipart_suggestion_verbose(
                     fluent::trait_selection_adjust_signature_borrow,
                     to_borrow,
@@ -123,7 +120,7 @@ impl AddToDiagnostic for AdjustSignatureBorrow {
                 );
             }
             AdjustSignatureBorrow::RemoveBorrow { remove_borrow } => {
-                diag.set_arg("len", remove_borrow.len());
+                diag.arg("len", remove_borrow.len());
                 diag.multipart_suggestion_verbose(
                     fluent::trait_selection_adjust_signature_remove_borrow,
                     remove_borrow,
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index 626569fb40f..c05c9961750 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -2,15 +2,29 @@
 //! Doing this via a separate goal is called "deferred alias relation" and part
 //! of our more general approach to "lazy normalization".
 //!
-//! This goal, e.g. `A alias-relate B`, may be satisfied by one of three branches:
-//! * normalizes-to: If `A` is a projection, we can prove the equivalent
-//!   projection predicate with B as the right-hand side of the projection.
-//!   This goal is computed in both directions, if both are aliases.
-//! * subst-relate: Equate `A` and `B` by their substs, if they're both
-//!   aliases with the same def-id.
-//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
-//!   may apply, then we can compute the "intersection" of both normalizes-to by
-//!   performing them together. This is used specifically to resolve ambiguities.
+//! This is done by first normalizing both sides of the goal, ending up in
+//! either a concrete type, rigid projection, opaque, or an infer variable.
+//! These are related further according to the rules below:
+//!
+//! (1.) If we end up with a rigid projection and a rigid projection, then we
+//! relate those projections structurally.
+//!
+//! (2.) If we end up with a rigid projection and an alias, then the opaque will
+//! have its hidden type defined to be that rigid projection.
+//!
+//! (3.) If we end up with an opaque and an opaque, then we assemble two
+//! candidates, one defining the LHS to be the hidden type of the RHS, and vice
+//! versa.
+//!
+//! (4.) If we end up with an infer var and an opaque or rigid projection, then
+//! we assign the alias to the infer var.
+//!
+//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we
+//! define the hidden type of the opaque to be the rigid type.
+//!
+//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types,
+//! relate them structurally.
+
 use super::{EvalCtxt, GoalSource};
 use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::query::NoSolution;
@@ -50,6 +64,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     self.relate(param_env, lhs, variance, rhs)?;
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 } else if alias.is_opaque(tcx) {
+                    // FIXME: This doesn't account for variance.
                     self.define_opaque(param_env, alias, rhs)
                 } else {
                     Err(NoSolution)
@@ -60,6 +75,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     self.relate(param_env, lhs, variance, rhs)?;
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                 } else if alias.is_opaque(tcx) {
+                    // FIXME: This doesn't account for variance.
                     self.define_opaque(param_env, alias, lhs)
                 } else {
                     Err(NoSolution)
@@ -72,6 +88,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
     /// Normalize the `term` to equate it later. This does not define opaque types.
     #[instrument(level = "debug", skip(self, param_env), ret)]
     fn try_normalize_term(
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index 2f3111a2414..7c8f885a1f2 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -22,6 +22,7 @@ use rustc_middle::traits::solve::{
     CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
     QueryResult, Response,
 };
+use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex};
 use rustc_middle::ty::{
     CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
@@ -316,19 +317,25 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return Some(ty);
         };
 
-        // We do no always define opaque types eagerly to allow non-defining uses in the defining scope.
-        if let (DefineOpaqueTypes::No, ty::AliasKind::Opaque) = (define_opaque_types, kind) {
-            if let Some(def_id) = alias.def_id.as_local() {
-                if self
-                    .unify_existing_opaque_tys(
-                        param_env,
-                        OpaqueTypeKey { def_id, args: alias.args },
-                        self.next_ty_infer(),
-                    )
-                    .is_empty()
-                {
-                    return Some(ty);
-                }
+        // We do no always define opaque types eagerly to allow non-defining uses
+        // in the defining scope. However, if we can unify this opaque to an existing
+        // opaque, then we should attempt to eagerly reveal the opaque, and we fall
+        // through.
+        if let DefineOpaqueTypes::No = define_opaque_types
+            && let Reveal::UserFacing = param_env.reveal()
+            && let ty::Opaque = kind
+            && let Some(def_id) = alias.def_id.as_local()
+            && self.can_define_opaque_ty(def_id)
+        {
+            if self
+                .unify_existing_opaque_tys(
+                    param_env,
+                    OpaqueTypeKey { def_id, args: alias.args },
+                    self.next_ty_infer(),
+                )
+                .is_empty()
+            {
+                return Some(ty);
             }
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 533fe32f70d..ecbb92ca5b9 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -10,7 +10,7 @@ use crate::solve::inspect::{InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor
 use crate::solve::{deeply_normalize_for_diagnostics, inspect};
 use crate::traits::engine::TraitEngineExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::select::{IntercrateAmbiguityCause, TreatInductiveCycleAs};
+use crate::traits::select::IntercrateAmbiguityCause;
 use crate::traits::structural_normalize::StructurallyNormalizeExt;
 use crate::traits::NormalizeExt;
 use crate::traits::SkipLeakCheck;
@@ -31,7 +31,6 @@ use rustc_middle::traits::DefiningAnchor;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::visit::{TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
-use rustc_session::lint::builtin::COINDUCTIVE_OVERLAP_IN_COHERENCE;
 use rustc_span::symbol::sym;
 use rustc_span::DUMMY_SP;
 use std::fmt::Debug;
@@ -197,7 +196,7 @@ fn overlap<'tcx>(
         .intercrate(true)
         .with_next_trait_solver(tcx.next_trait_solver_in_coherence())
         .build();
-    let selcx = &mut SelectionContext::new(&infcx);
+    let selcx = &mut SelectionContext::with_treat_inductive_cycle_as_ambig(&infcx);
     if track_ambiguity_causes.is_yes() {
         selcx.enable_tracking_intercrate_ambiguity_causes();
     }
@@ -224,61 +223,10 @@ fn overlap<'tcx>(
     );
 
     if overlap_mode.use_implicit_negative() {
-        for mode in [TreatInductiveCycleAs::Ambig, TreatInductiveCycleAs::Recur] {
-            if let Some(failing_obligation) = selcx.with_treat_inductive_cycle_as(mode, |selcx| {
-                impl_intersection_has_impossible_obligation(selcx, &obligations)
-            }) {
-                if matches!(mode, TreatInductiveCycleAs::Recur) {
-                    let first_local_impl = impl1_header
-                        .impl_def_id
-                        .as_local()
-                        .or(impl2_header.impl_def_id.as_local())
-                        .expect("expected one of the impls to be local");
-                    infcx.tcx.struct_span_lint_hir(
-                        COINDUCTIVE_OVERLAP_IN_COHERENCE,
-                        infcx.tcx.local_def_id_to_hir_id(first_local_impl),
-                        infcx.tcx.def_span(first_local_impl),
-                        format!(
-                            "implementations {} will conflict in the future",
-                            match impl1_header.trait_ref {
-                                Some(trait_ref) => {
-                                    let trait_ref = infcx.resolve_vars_if_possible(trait_ref);
-                                    format!(
-                                        "of `{}` for `{}`",
-                                        trait_ref.print_trait_sugared(),
-                                        trait_ref.self_ty()
-                                    )
-                                }
-                                None => format!(
-                                    "for `{}`",
-                                    infcx.resolve_vars_if_possible(impl1_header.self_ty)
-                                ),
-                            },
-                        ),
-                        |lint| {
-                            lint.note(
-                                "impls that are not considered to overlap may be considered to \
-                                overlap in the future",
-                            )
-                            .span_label(
-                                infcx.tcx.def_span(impl1_header.impl_def_id),
-                                "the first impl is here",
-                            )
-                            .span_label(
-                                infcx.tcx.def_span(impl2_header.impl_def_id),
-                                "the second impl is here",
-                            );
-                            lint.note(format!(
-                                "`{}` may be considered to hold in future releases, \
-                                    causing the impls to overlap",
-                                infcx.resolve_vars_if_possible(failing_obligation.predicate)
-                            ));
-                        },
-                    );
-                }
-
-                return None;
-            }
+        if let Some(_failing_obligation) =
+            impl_intersection_has_impossible_obligation(selcx, &obligations)
+        {
+            return None;
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 451e0823c25..522c645253a 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -122,8 +122,8 @@ pub fn is_const_evaluatable<'tcx>(
                         if span == rustc_span::DUMMY_SP { tcx.def_span(uv.def) } else { span },
                         "failed to evaluate generic const expression",
                     )
-                    .note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
-                    .span_suggestion_verbose(
+                    .with_note("the crate this constant originates from uses `#![feature(generic_const_exprs)]`")
+                    .with_span_suggestion_verbose(
                         rustc_span::DUMMY_SP,
                         "consider enabling this feature",
                         "#![feature(generic_const_exprs)]\n",
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
index 90e2c1531b4..864580afe62 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
@@ -1,7 +1,7 @@
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::InferCtxt;
 use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::Node;
 use rustc_middle::ty::{self, Ty};
@@ -140,7 +140,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         let expected_str = args_str(&expected_args, &found_args);
         let found_str = args_str(&found_args, &expected_args);
 
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0593,
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 52f91d282f0..100c9a70aaa 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
@@ -6,7 +6,7 @@ use rustc_ast::AttrKind;
 use rustc_ast::{Attribute, MetaItem, NestedMetaItem};
 use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_errors::{struct_span_code_err, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::GenericArgsRef;
@@ -14,7 +14,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
 use rustc_parse_format::{ParseMode, Parser, Piece, Position};
 use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
 use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use std::iter;
 
 use crate::errors::{
@@ -521,7 +521,7 @@ impl<'tcx> OnUnimplementedDirective {
     pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
         if let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) {
             return Self::parse_attribute(attr, false, tcx, item_def_id);
-        } else if tcx.features().diagnostic_namespace {
+        } else {
             tcx.get_attrs_by_path(item_def_id, &[sym::diagnostic, sym::on_unimplemented])
                 .filter_map(|attr| Self::parse_attribute(attr, true, tcx, item_def_id).transpose())
                 .try_fold(None, |aggr: Option<Self>, directive| {
@@ -592,8 +592,6 @@ impl<'tcx> OnUnimplementedDirective {
                         Ok(Some(directive))
                     }
                 })
-        } else {
-            Ok(None)
         }
     }
 
@@ -659,9 +657,7 @@ impl<'tcx> OnUnimplementedDirective {
 
             Ok(None)
         } else {
-            let reported = tcx
-                .dcx()
-                .span_delayed_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
+            let reported = tcx.dcx().delayed_bug("of_item: neither meta_item_list nor value_str");
             return Err(reported);
         };
         debug!("of_item({:?}) = {:?}", item_def_id, result);
@@ -799,7 +795,7 @@ impl<'tcx> OnUnimplementedFormatString {
                                         },
                                     );
                                 } else {
-                                    result = Err(struct_span_err!(
+                                    result = Err(struct_span_code_err!(
                                         tcx.dcx(),
                                         self.span,
                                         E0230,
@@ -818,7 +814,7 @@ impl<'tcx> OnUnimplementedFormatString {
                     }
                     // `{:1}` and `{}` are not to be used
                     Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
-                        let reported = struct_span_err!(
+                        let reported = struct_span_code_err!(
                             tcx.dcx(),
                             self.span,
                             E0231,
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 f63314081d6..6c0909d2853 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -13,17 +13,16 @@ use hir::def::CtorOf;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
+    error_code, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
     MultiSpan, Style, SuggestionStyle,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
+use rustc_hir::intravisit::{Map, Visitor};
 use rustc_hir::is_range_literal;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Node};
-use rustc_hir::{Expr, HirId};
+use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk};
@@ -2008,7 +2007,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         };
 
         err.code(error_code!(E0746));
-        err.set_primary_message("return type cannot have an unboxed trait object");
+        err.primary_message("return type cannot have an unboxed trait object");
         err.children.clear();
 
         let span = obligation.cause.span;
@@ -2089,7 +2088,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     let ty = self.resolve_vars_if_possible(returned_ty);
                     if ty.references_error() {
                         // don't print out the [type error] here
-                        err.delay_as_bug();
+                        err.downgrade_to_delayed_bug();
                     } else {
                         err.span_label(expr.span, format!("this returned value is of type `{ty}`"));
                     }
@@ -2146,7 +2145,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             ty::Coroutine(..) => "coroutine",
             _ => "function",
         };
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0631,
@@ -2713,7 +2712,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") };
 
             err.clear_code();
-            err.set_primary_message(format!(
+            err.primary_message(format!(
                 "{future_or_coroutine} cannot be {trait_verb} between threads safely"
             ));
 
@@ -2801,7 +2800,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}"));
 
             span.push_span_label(original_span, message);
-            err.set_span(span);
+            err.span(span);
 
             format!("is not {trait_name}")
         } else {
@@ -3200,35 +3199,80 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     err.help("unsized locals are gated as an unstable feature");
                 }
             }
-            ObligationCauseCode::SizedArgumentType(ty_span) => {
-                if let Some(span) = ty_span {
-                    if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
-                        && let ty::ClauseKind::Trait(trait_pred) = clause
-                        && let ty::Dynamic(..) = trait_pred.self_ty().kind()
-                    {
-                        let span = if let Ok(snippet) =
-                            self.tcx.sess.source_map().span_to_snippet(span)
-                            && snippet.starts_with("dyn ")
-                        {
-                            let pos = snippet.len() - snippet[3..].trim_start().len();
-                            span.with_hi(span.lo() + BytePos(pos as u32))
-                        } else {
-                            span.shrink_to_lo()
-                        };
-                        err.span_suggestion_verbose(
-                            span,
-                            "you can use `impl Trait` as the argument type",
-                            "impl ".to_string(),
-                            Applicability::MaybeIncorrect,
-                        );
+            ObligationCauseCode::SizedArgumentType(hir_id) => {
+                let mut ty = None;
+                let borrowed_msg = "function arguments must have a statically known size, borrowed \
+                                    types always have a known size";
+                if let Some(hir_id) = hir_id
+                    && let Some(hir::Node::Param(param)) = self.tcx.hir().find(hir_id)
+                    && let Some(item) = self.tcx.hir().find_parent(hir_id)
+                    && let Some(decl) = item.fn_decl()
+                    && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span))
+                {
+                    // We use `contains` because the type might be surrounded by parentheses,
+                    // which makes `ty_span` and `t.span` disagree with each other, but one
+                    // fully contains the other: `foo: (dyn Foo + Bar)`
+                    //                                 ^-------------^
+                    //                                 ||
+                    //                                 |t.span
+                    //                                 param._ty_span
+                    ty = Some(t);
+                } else if let Some(hir_id) = hir_id
+                    && let Some(hir::Node::Ty(t)) = self.tcx.hir().find(hir_id)
+                {
+                    ty = Some(t);
+                }
+                if let Some(ty) = ty {
+                    match ty.kind {
+                        hir::TyKind::TraitObject(traits, _, _) => {
+                            let (span, kw) = match traits {
+                                [first, ..] if first.span.lo() == ty.span.lo() => {
+                                    // Missing `dyn` in front of trait object.
+                                    (ty.span.shrink_to_lo(), "dyn ")
+                                }
+                                [first, ..] => (ty.span.until(first.span), ""),
+                                [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"),
+                            };
+                            let needs_parens = traits.len() != 1;
+                            err.span_suggestion_verbose(
+                                span,
+                                "you can use `impl Trait` as the argument type",
+                                "impl ".to_string(),
+                                Applicability::MaybeIncorrect,
+                            );
+                            let sugg = if !needs_parens {
+                                vec![(span.shrink_to_lo(), format!("&{kw}"))]
+                            } else {
+                                vec![
+                                    (span.shrink_to_lo(), format!("&({kw}")),
+                                    (ty.span.shrink_to_hi(), ")".to_string()),
+                                ]
+                            };
+                            err.multipart_suggestion_verbose(
+                                borrowed_msg,
+                                sugg,
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        hir::TyKind::Slice(_ty) => {
+                            err.span_suggestion_verbose(
+                                ty.span.shrink_to_lo(),
+                                "function arguments must have a statically known size, borrowed \
+                                 slices always have a known size",
+                                "&",
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        hir::TyKind::Path(_) => {
+                            err.span_suggestion_verbose(
+                                ty.span.shrink_to_lo(),
+                                borrowed_msg,
+                                "&",
+                                Applicability::MachineApplicable,
+                            );
+                        }
+                        _ => {}
                     }
-                    err.span_suggestion_verbose(
-                        span.shrink_to_lo(),
-                        "function arguments must have a statically known size, borrowed types \
-                         always have a known size",
-                        "&",
-                        Applicability::MachineApplicable,
-                    );
                 } else {
                     err.note("all function arguments must have a statically known size");
                 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index d2598b0defe..47a700805fa 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -19,7 +19,7 @@ use crate::traits::{
 };
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{
-    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+    pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
     MultiSpan, StashKey, Style,
 };
 use rustc_hir as hir;
@@ -228,7 +228,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         }
 
-        self.dcx().span_delayed_bug(DUMMY_SP, "expected fulfillment errors")
+        self.dcx().delayed_bug("expected fulfillment errors")
     }
 
     /// Reports that an overflow has occurred and halts compilation. We
@@ -280,7 +280,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             predicate.print(&mut cx).unwrap();
             pred_str = cx.into_buffer();
         }
-        let mut err = struct_span_err!(
+        let mut err = struct_span_code_err!(
             self.dcx(),
             span,
             E0275,
@@ -525,7 +525,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                             (err_msg, None)
                         };
 
-                        let mut err = struct_span_err!(self.dcx(), span, E0277, "{}", err_msg);
+                        let mut err = struct_span_code_err!(self.dcx(), span, E0277, "{}", err_msg);
 
                         let mut suggested = false;
                         if is_try_conversion {
@@ -816,7 +816,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
                     ty::PredicateKind::ObjectSafe(trait_def_id) => {
                         let violations = self.tcx.object_safety_violations(trait_def_id);
-                        report_object_safety_error(self.tcx, span, trait_def_id, violations)
+                        report_object_safety_error(self.tcx, span, None, trait_def_id, violations)
                     }
 
                     ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(ty)) => {
@@ -924,7 +924,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
             TraitNotObjectSafe(did) => {
                 let violations = self.tcx.object_safety_violations(did);
-                report_object_safety_error(self.tcx, span, did, violations)
+                report_object_safety_error(self.tcx, span, None, did, violations)
             }
 
             SelectionError::NotConstEvaluatable(NotConstEvaluatable::MentionsInfer) => {
@@ -1236,7 +1236,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 span_bug!(span, "const param tys cannot mention other generic parameters");
             }
             ty::Float(_) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0741,
@@ -1244,7 +1244,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 )
             }
             ty::FnPtr(_) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0741,
@@ -1252,7 +1252,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 )
             }
             ty::RawPtr(_) => {
-                struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0741,
@@ -1261,7 +1261,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
             ty::Adt(def, _) => {
                 // We should probably see if we're *allowed* to derive `ConstParamTy` on the type...
-                let mut diag = struct_span_err!(
+                let mut diag = struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0741,
@@ -1293,7 +1293,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                 diag
             }
             _ => {
-                struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0741,
@@ -1731,7 +1731,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         cx.into_buffer()
                     }))
                 });
-            let mut diag = struct_span_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
+            let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
 
             let secondary_span = (|| {
                 let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
@@ -2421,7 +2421,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         true,
                     )
                 } else {
-                    struct_span_err!(
+                    struct_span_code_err!(
                         self.dcx(),
                         span,
                         E0283,
@@ -2525,14 +2525,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
                         // Replace the more general E0283 with a more specific error
                         err.cancel();
-                        err = self.dcx().struct_span_err_with_code(
+                        err = self.dcx().struct_span_err(
                             span,
                             format!(
                                 "cannot {verb} associated {noun} on trait without specifying the \
                                  corresponding `impl` type",
                             ),
-                            rustc_errors::error_code!(E0790),
                         );
+                        err.code(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 {
@@ -2654,26 +2654,24 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     .chain(Some(data.term.into_arg()))
                     .find(|g| g.has_non_region_infer());
                 if let Some(subst) = subst {
-                    let mut err = self.emit_inference_failure_err(
+                    self.emit_inference_failure_err(
                         obligation.cause.body_id,
                         span,
                         subst,
                         ErrorCode::E0284,
                         true,
-                    );
-                    err.note(format!("cannot satisfy `{predicate}`"));
-                    err
+                    )
+                    .with_note(format!("cannot satisfy `{predicate}`"))
                 } else {
                     // If we can't find a substitution, just print a generic error
-                    let mut err = struct_span_err!(
+                    struct_span_code_err!(
                         self.dcx(),
                         span,
                         E0284,
                         "type annotations needed: cannot satisfy `{}`",
                         predicate,
-                    );
-                    err.span_label(span, format!("cannot satisfy `{predicate}`"));
-                    err
+                    )
+                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
                 }
             }
 
@@ -2693,30 +2691,28 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     err
                 } else {
                     // If we can't find a substitution, just print a generic error
-                    let mut err = struct_span_err!(
+                    struct_span_code_err!(
                         self.dcx(),
                         span,
                         E0284,
                         "type annotations needed: cannot satisfy `{}`",
                         predicate,
-                    );
-                    err.span_label(span, format!("cannot satisfy `{predicate}`"));
-                    err
+                    )
+                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
                 }
             }
             _ => {
                 if self.dcx().has_errors().is_some() || self.tainted_by_errors().is_some() {
                     return;
                 }
-                let mut err = struct_span_err!(
+                struct_span_code_err!(
                     self.dcx(),
                     span,
                     E0284,
                     "type annotations needed: cannot satisfy `{}`",
                     predicate,
-                );
-                err.span_label(span, format!("cannot satisfy `{predicate}`"));
-                err
+                )
+                .with_span_label(span, format!("cannot satisfy `{predicate}`"))
             }
         };
         self.note_obligation_cause(&mut err, obligation);
@@ -3163,14 +3159,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     ) {
         match obligation_cause_code {
             ObligationCauseCode::RustCall => {
-                err.set_primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
+                err.primary_message("functions with the \"rust-call\" ABI must take a single non-self tuple argument");
             }
             ObligationCauseCode::BindingObligation(def_id, _)
             | ObligationCauseCode::ItemObligation(def_id)
                 if self.tcx.is_fn_trait(*def_id) =>
             {
                 err.code(rustc_errors::error_code!(E0059));
-                err.set_primary_message(format!(
+                err.primary_message(format!(
                     "type parameter to bare `{}` trait must be a tuple",
                     self.tcx.def_path_str(*def_id)
                 ));
@@ -3548,17 +3544,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         span: Span,
     ) -> Option<DiagnosticBuilder<'tcx>> {
         if !self.tcx.features().generic_const_exprs {
-            let mut err = self
-                .dcx()
-                .struct_span_err(span, "constant expression depends on a generic parameter");
-            // FIXME(const_generics): we should suggest to the user how they can resolve this
-            // issue. However, this is currently not actually possible
-            // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
-            //
-            // Note that with `feature(generic_const_exprs)` this case should not
-            // be reachable.
-            err.note("this may fail depending on what value the parameter takes");
-            err.emit();
+            self.dcx()
+                .struct_span_err(span, "constant expression depends on a generic parameter")
+                // FIXME(const_generics): we should suggest to the user how they can resolve this
+                // issue. However, this is currently not actually possible
+                // (see https://github.com/rust-lang/rust/issues/66962#issuecomment-575907083).
+                //
+                // Note that with `feature(generic_const_exprs)` this case should not
+                // be reachable.
+                .with_note("this may fail depending on what value the parameter takes")
+                .emit();
             return None;
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index c52b8f37ef3..080ad7bd549 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -279,6 +279,12 @@ pub fn normalize_param_env_or_error<'tcx>(
                 }
 
                 fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> {
+                    // FIXME(return_type_notation): track binders in this normalizer, as
+                    // `ty::Const::normalize` can only work with properly preserved binders.
+
+                    if c.has_escaping_bound_vars() {
+                        return ty::Const::new_misc_error(self.0, c.ty());
+                    }
                     // While it is pretty sus to be evaluating things with an empty param env, it
                     // should actually be okay since without `feature(generic_const_exprs)` the only
                     // const arguments that have a non-empty param env are array repeat counts. These
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 9749ff9cbc1..5b349d576b6 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -65,8 +65,7 @@ impl<'a, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'tcx> {
         assert!(!ty.has_non_region_infer());
 
         let mut canonical_var_values = OriginalQueryValues::default();
-        let canonical_ty =
-            self.canonicalize_query_keep_static(param_env.and(ty), &mut canonical_var_values);
+        let canonical_ty = self.canonicalize_query(param_env.and(ty), &mut canonical_var_values);
         let Ok(canonical_result) = self.tcx.implied_outlives_bounds(canonical_ty) else {
             return vec![];
         };
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 d812d537d8c..31e34096fb0 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -87,10 +87,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
             })
         } else {
             assert!(!self.intercrate);
-            let c_pred = self.canonicalize_query_keep_static(
-                param_env.and(obligation.predicate),
-                &mut _orig_values,
-            );
+            let c_pred =
+                self.canonicalize_query(param_env.and(obligation.predicate), &mut _orig_values);
             self.tcx.at(obligation.cause.span()).evaluate_obligation(c_pred)
         }
     }
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index e8867187a40..0b73fefd2da 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -239,16 +239,13 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                         }
 
                         let generic_ty = self.interner().type_of(data.def_id);
-                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        let mut concrete_ty = generic_ty.instantiate(self.interner(), args);
                         self.anon_depth += 1;
                         if concrete_ty == ty {
-                            bug!(
-                                "infinite recursion generic_ty: {:#?}, args: {:#?}, \
-                                 concrete_ty: {:#?}, ty: {:#?}",
-                                generic_ty,
-                                args,
-                                concrete_ty,
-                                ty
+                            concrete_ty = Ty::new_error_with_message(
+                                self.interner(),
+                                DUMMY_SP,
+                                "recursive opaque type",
                             );
                         }
                         let folded_ty = ensure_sufficient_stack(|| self.try_fold_ty(concrete_ty));
@@ -275,10 +272,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 let data = data.try_fold_with(self)?;
 
                 let mut orig_values = OriginalQueryValues::default();
-                // HACK(matthewjasper) `'static` is special-cased in selection,
-                // so we cannot canonicalize it.
-                let c_data = infcx
-                    .canonicalize_query_keep_static(self.param_env.and(data), &mut orig_values);
+                let c_data = infcx.canonicalize_query(self.param_env.and(data), &mut orig_values);
                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                 let result = match kind {
@@ -292,10 +286,8 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                     // Rustdoc normalizes possibly not well-formed types, so only
                     // treat this as a bug if we're not in rustdoc.
                     if !tcx.sess.opts.actually_rustdoc {
-                        tcx.dcx().span_delayed_bug(
-                            DUMMY_SP,
-                            format!("unexpected ambiguity: {c_data:?} {result:?}"),
-                        );
+                        tcx.dcx()
+                            .delayed_bug(format!("unexpected ambiguity: {c_data:?} {result:?}"));
                     }
                     return Err(NoSolution);
                 }
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 f4baae2711f..d533e69a4fa 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
@@ -6,7 +6,7 @@ use rustc_errors::ErrorGuaranteed;
 use rustc_infer::infer::region_constraints::RegionConstraintData;
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::ty::{TyCtxt, TypeFoldable};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 
 use std::fmt;
 
@@ -88,10 +88,9 @@ where
         if errors.is_empty() {
             Ok(value)
         } else {
-            Err(infcx.dcx().span_delayed_bug(
-                DUMMY_SP,
-                format!("errors selecting obligation during MIR typeck: {errors:?}"),
-            ))
+            Err(infcx
+                .dcx()
+                .delayed_bug(format!("errors selecting obligation during MIR typeck: {errors:?}")))
         }
     })?;
 
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 cab2a62ed7e..957de925dd3 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
@@ -111,14 +111,9 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable<TyCtxt<'tcx>> + 't
             return Ok((result, None, vec![], Certainty::Proven));
         }
 
-        // FIXME(#33684) -- We need to use
-        // `canonicalize_query_keep_static` here because of things
-        // like the subtype query, which go awry around
-        // `'static` otherwise.
         let mut canonical_var_values = OriginalQueryValues::default();
         let old_param_env = query_key.param_env;
-        let canonical_self =
-            infcx.canonicalize_query_keep_static(query_key, &mut canonical_var_values);
+        let canonical_self = infcx.canonicalize_query(query_key, &mut canonical_var_values);
         let canonical_result = Self::perform_query(infcx.tcx, canonical_self)?;
 
         let InferOk { value, obligations } = infcx
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 54b91ab1d4d..342b12ba498 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -969,7 +969,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                                     self.tcx().def_span(impl_def_id),
                                     "multiple drop impls found",
                                 )
-                                .span_note(self.tcx().def_span(old_impl_def_id), "other impl here")
+                                .with_span_note(
+                                    self.tcx().def_span(old_impl_def_id),
+                                    "other impl here",
+                                )
                                 .delay_as_bug();
                         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index c45925295ee..61fe2c8efe3 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -239,20 +239,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
-    // Sets the `TreatInductiveCycleAs` mode temporarily in the selection context
-    pub fn with_treat_inductive_cycle_as<T>(
-        &mut self,
-        treat_inductive_cycle: TreatInductiveCycleAs,
-        f: impl FnOnce(&mut Self) -> T,
-    ) -> T {
+    pub fn with_treat_inductive_cycle_as_ambig(
+        infcx: &'cx InferCtxt<'tcx>,
+    ) -> SelectionContext<'cx, 'tcx> {
         // Should be executed in a context where caching is disabled,
         // otherwise the cache is poisoned with the temporary result.
-        assert!(self.is_intercrate());
-        let treat_inductive_cycle =
-            std::mem::replace(&mut self.treat_inductive_cycle, treat_inductive_cycle);
-        let value = f(self);
-        self.treat_inductive_cycle = treat_inductive_cycle;
-        value
+        assert!(infcx.intercrate);
+        SelectionContext {
+            treat_inductive_cycle: TreatInductiveCycleAs::Ambig,
+            ..SelectionContext::new(infcx)
+        }
     }
 
     pub fn with_query_mode(
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index d43ab0c8e85..10329c623b3 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -345,7 +345,7 @@ fn report_negative_positive_conflict<'tcx>(
     positive_impl_def_id: DefId,
     sg: &mut specialization_graph::Graph,
 ) {
-    let mut err = tcx.dcx().create_err(NegativePositiveConflict {
+    let err = tcx.dcx().create_err(NegativePositiveConflict {
         impl_span: tcx.def_span(local_impl_def_id),
         trait_desc: overlap.trait_ref,
         self_ty: overlap.self_ty,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index 19eae93df9c..c40ed10e52f 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -271,7 +271,7 @@ pub fn closure_trait_ref_and_return_type<'tcx>(
         TupleArgumentsFlag::No => sig.skip_binder().inputs()[0],
         TupleArgumentsFlag::Yes => Ty::new_tup(tcx, sig.skip_binder().inputs()),
     };
-    let trait_ref = if tcx.generics_of(fn_trait_def_id).host_effect_index.is_some() {
+    let trait_ref = if tcx.has_host_param(fn_trait_def_id) {
         ty::TraitRef::new(
             tcx,
             fn_trait_def_id,
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index 49f24f66b24..86a077ee808 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -199,6 +199,7 @@ pub(crate) mod rustc {
             match err {
                 LayoutError::Unknown(..) | LayoutError::ReferencesError(..) => Self::UnknownLayout,
                 LayoutError::SizeOverflow(..) => Self::SizeOverflow,
+                LayoutError::Cycle(err) => Self::TypeError(*err),
                 err => unimplemented!("{:?}", err),
             }
         }
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index db89fba2a89..b8351463c55 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -11,7 +11,6 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_session::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
 use rustc_span::symbol::Symbol;
-use rustc_span::DUMMY_SP;
 use rustc_target::abi::*;
 
 use std::fmt::Debug;
@@ -91,7 +90,7 @@ fn univariant_uninterned<'tcx>(
     let dl = cx.data_layout();
     let pack = repr.pack;
     if pack.is_some() && repr.align.is_some() {
-        cx.tcx.dcx().span_delayed_bug(DUMMY_SP, "struct cannot be packed and aligned");
+        cx.tcx.dcx().delayed_bug("struct cannot be packed and aligned");
         return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
     }
 
@@ -344,10 +343,7 @@ fn layout_of_uncached<'tcx>(
         ty::Adt(def, args) if def.repr().simd() => {
             if !def.is_struct() {
                 // Should have yielded E0517 by now.
-                tcx.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    "#[repr(simd)] was applied to an ADT that is not a struct",
-                );
+                tcx.dcx().delayed_bug("#[repr(simd)] was applied to an ADT that is not a struct");
                 return Err(error(cx, LayoutError::Unknown(ty)));
             }
 
@@ -374,8 +370,7 @@ fn layout_of_uncached<'tcx>(
             // (should be caught by typeck)
             for fi in fields {
                 if fi.ty(tcx, args) != f0_ty {
-                    tcx.dcx().span_delayed_bug(
-                        DUMMY_SP,
+                    tcx.dcx().delayed_bug(
                         "#[repr(simd)] was applied to an ADT with heterogeneous field type",
                     );
                     return Err(error(cx, LayoutError::Unknown(ty)));
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index 38f0eb82180..59966423f7f 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -11,6 +11,7 @@ rustc_data_structures = { path = "../rustc_data_structures", optional = true }
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
+rustc_span = { path = "../rustc_span", optional = true }
 smallvec = { version = "1.8.1" }
 # tidy-alphabetical-end
 
@@ -21,6 +22,7 @@ nightly = [
     "smallvec/union",
     "rustc_index/nightly",
     "rustc_serialize",
+    "rustc_span",
     "rustc_data_structures",
     "rustc_macros",
 ]
diff --git a/compiler/rustc_type_ir/src/codec.rs b/compiler/rustc_type_ir/src/codec.rs
index 2fbc8f76fa4..71f9eb0ef8a 100644
--- a/compiler/rustc_type_ir/src/codec.rs
+++ b/compiler/rustc_type_ir/src/codec.rs
@@ -1,7 +1,7 @@
 use crate::{Interner, PredicateKind};
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_serialize::{Decoder, Encoder};
+use rustc_span::{SpanDecoder, SpanEncoder};
 
 /// The shorthand encoding uses an enum's variant index `usize`
 /// and is offset by this value so it never matches a real variant.
@@ -22,7 +22,7 @@ pub trait RefDecodable<'tcx, D: TyDecoder> {
     fn decode(d: &mut D) -> &'tcx Self;
 }
 
-pub trait TyEncoder: Encoder {
+pub trait TyEncoder: SpanEncoder {
     type I: Interner;
     const CLEAR_CROSS_CRATE: bool;
 
@@ -35,7 +35,7 @@ pub trait TyEncoder: Encoder {
     fn encode_alloc_id(&mut self, alloc_id: &<Self::I as Interner>::AllocId);
 }
 
-pub trait TyDecoder: Decoder {
+pub trait TyDecoder: SpanDecoder {
     type I: Interner;
     const CLEAR_CROSS_CRATE: bool;
 
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index f52e506059b..fb83dae5714 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -16,8 +16,8 @@ use crate::ty::{
     TraitDef, Ty, TyKind, VariantDef,
 };
 use crate::{
-    mir, Crate, CrateItem, CrateItems, DefId, Error, Filename, ImplTraitDecls, ItemKind, Symbol,
-    TraitDecls,
+    mir, Crate, CrateItem, CrateItems, CrateNum, DefId, Error, Filename, ImplTraitDecls, ItemKind,
+    Symbol, TraitDecls,
 };
 
 /// This trait defines the interface between stable_mir and the Rust compiler.
@@ -32,8 +32,10 @@ pub trait Context {
     /// Check whether the body of a function is available.
     fn has_body(&self, item: DefId) -> bool;
     fn all_trait_decls(&self) -> TraitDecls;
+    fn trait_decls(&self, crate_num: CrateNum) -> TraitDecls;
     fn trait_decl(&self, trait_def: &TraitDef) -> TraitDecl;
     fn all_trait_impls(&self) -> ImplTraitDecls;
+    fn trait_impls(&self, crate_num: CrateNum) -> ImplTraitDecls;
     fn trait_impl(&self, trait_impl: &ImplDef) -> ImplTrait;
     fn generics_of(&self, def_id: DefId) -> Generics;
     fn predicates_of(&self, def_id: DefId) -> GenericPredicates;
diff --git a/compiler/stable_mir/src/lib.rs b/compiler/stable_mir/src/lib.rs
index 9194f1e6bdb..de5dfcdf207 100644
--- a/compiler/stable_mir/src/lib.rs
+++ b/compiler/stable_mir/src/lib.rs
@@ -31,7 +31,7 @@ pub use crate::error::*;
 use crate::mir::pretty::function_name;
 use crate::mir::Body;
 use crate::mir::Mutability;
-use crate::ty::{ImplDef, ImplTrait, IndexedVal, Span, TraitDecl, TraitDef, Ty};
+use crate::ty::{ImplDef, IndexedVal, Span, TraitDef, Ty};
 
 pub mod abi;
 #[macro_use]
@@ -86,6 +86,18 @@ pub struct Crate {
     pub is_local: bool,
 }
 
+impl Crate {
+    /// The list of traits declared in this crate.
+    pub fn trait_decls(&self) -> TraitDecls {
+        with(|cx| cx.trait_decls(self.id))
+    }
+
+    /// The list of trait implementations in this crate.
+    pub fn trait_impls(&self) -> ImplTraitDecls {
+        with(|cx| cx.trait_impls(self.id))
+    }
+}
+
 #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
 pub enum ItemKind {
     Fn,
@@ -169,18 +181,10 @@ pub fn all_trait_decls() -> TraitDecls {
     with(|cx| cx.all_trait_decls())
 }
 
-pub fn trait_decl(trait_def: &TraitDef) -> TraitDecl {
-    with(|cx| cx.trait_decl(trait_def))
-}
-
 pub fn all_trait_impls() -> ImplTraitDecls {
     with(|cx| cx.all_trait_impls())
 }
 
-pub fn trait_impl(trait_impl: &ImplDef) -> ImplTrait {
-    with(|cx| cx.trait_impl(trait_impl))
-}
-
 /// A type that provides internal information but that can still be used for debug purpose.
 #[derive(Clone, PartialEq, Eq, Hash)]
 pub struct Opaque(String);
diff --git a/compiler/stable_mir/src/ty.rs b/compiler/stable_mir/src/ty.rs
index 9e6ecbe8315..eba2ac57012 100644
--- a/compiler/stable_mir/src/ty.rs
+++ b/compiler/stable_mir/src/ty.rs
@@ -714,9 +714,16 @@ crate_def! {
 }
 
 crate_def! {
+    /// A trait's definition.
     pub TraitDef;
 }
 
+impl TraitDef {
+    pub fn declaration(trait_def: &TraitDef) -> TraitDecl {
+        with(|cx| cx.trait_decl(trait_def))
+    }
+}
+
 crate_def! {
     pub GenericDef;
 }
@@ -726,9 +733,17 @@ crate_def! {
 }
 
 crate_def! {
+    /// A trait impl definition.
     pub ImplDef;
 }
 
+impl ImplDef {
+    /// Retrieve information about this implementation.
+    pub fn trait_impl(&self) -> ImplTrait {
+        with(|cx| cx.trait_impl(self))
+    }
+}
+
 crate_def! {
     pub RegionDef;
 }