about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_abi/src/layout.rs4
-rw-r--r--compiler/rustc_abi/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/ast.rs73
-rw-r--r--compiler/rustc_ast/src/ast_traits.rs33
-rw-r--r--compiler/rustc_ast/src/attr/mod.rs17
-rw-r--r--compiler/rustc_ast/src/lib.rs2
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs6
-rw-r--r--compiler/rustc_ast/src/tokenstream.rs80
-rw-r--r--compiler/rustc_ast/src/util/classify.rs81
-rw-r--r--compiler/rustc_ast/src/visit.rs3
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl5
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs11
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs8
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs130
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs116
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs24
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs60
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs2
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs24
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs5
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state/expr.rs10
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs50
-rw-r--r--compiler/rustc_borrowck/src/constraints/mod.rs106
-rw-r--r--compiler/rustc_borrowck/src/dataflow.rs40
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs69
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs142
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs35
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs41
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs27
-rw-r--r--compiler/rustc_borrowck/src/facts.rs28
-rw-r--r--compiler/rustc_borrowck/src/lib.rs94
-rw-r--r--compiler/rustc_borrowck/src/nll.rs12
-rw-r--r--compiler/rustc_borrowck/src/polonius/loan_invalidations.rs6
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs101
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs15
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/mod.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/liveness/trace.rs16
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs89
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs9
-rw-r--r--compiler/rustc_borrowck/src/used_muts.rs4
-rw-r--r--compiler/rustc_builtin_macros/messages.ftl11
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs13
-rw-r--r--compiler/rustc_builtin_macros/src/assert/context.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs20
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/default.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs39
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs40
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs24
-rw-r--r--compiler/rustc_builtin_macros/src/source_util.rs11
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml4
-rw-r--r--compiler/rustc_codegen_cranelift/.github/workflows/main.yml17
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.lock58
-rw-r--r--compiler/rustc_codegen_cranelift/Cargo.toml14
-rw-r--r--compiler/rustc_codegen_cranelift/Readme.md3
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs35
-rw-r--r--compiler/rustc_codegen_cranelift/build_system/tests.rs36
-rw-r--r--compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/example/std_example.rs38
-rw-r--r--compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml33
-rw-r--r--compiler/rustc_codegen_cranelift/rust-toolchain2
-rwxr-xr-xcompiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh30
-rw-r--r--compiler/rustc_codegen_cranelift/src/abi/mod.rs146
-rw-r--r--compiler/rustc_codegen_cranelift/src/allocator.rs12
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs10
-rw-r--r--compiler/rustc_codegen_cranelift/src/common.rs4
-rw-r--r--compiler/rustc_codegen_cranelift/src/constant.rs9
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/aot.rs31
-rw-r--r--compiler/rustc_codegen_cranelift/src/driver/jit.rs52
-rw-r--r--compiler/rustc_codegen_cranelift/src/inline_asm.rs16
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs41
-rw-r--r--compiler/rustc_codegen_cranelift/src/lib.rs20
-rw-r--r--compiler/rustc_codegen_cranelift/src/main_shim.rs13
-rw-r--r--compiler/rustc_codegen_cranelift/src/unwind_module.rs115
-rw-r--r--compiler/rustc_codegen_gcc/src/common.rs28
-rw-r--r--compiler/rustc_codegen_gcc/src/context.rs11
-rw-r--r--compiler/rustc_codegen_gcc/src/type_.rs50
-rw-r--r--compiler/rustc_codegen_llvm/src/abi.rs3
-rw-r--r--compiler/rustc_codegen_llvm/src/attributes.rs31
-rw-r--r--compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/common.rs8
-rw-r--r--compiler/rustc_codegen_llvm/src/context.rs21
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs57
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs16
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs24
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs2
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs30
-rw-r--r--compiler/rustc_codegen_llvm/src/type_.rs28
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs117
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs742
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/back/rpath/tests.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/codegen_attrs.rs80
-rw-r--r--compiler/rustc_codegen_ssa/src/meth.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs87
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/locals.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs18
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs10
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/backend.rs20
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/consts.rs1
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/misc.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/mod.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/type_.rs6
-rw-r--r--compiler/rustc_const_eval/src/check_consts/check.rs19
-rw-r--r--compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs1
-rw-r--r--compiler/rustc_const_eval/src/const_eval/fn_queries.rs5
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs1
-rw-r--r--compiler/rustc_const_eval/src/interpret/cast.rs27
-rw-r--r--compiler/rustc_const_eval/src/interpret/discriminant.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs159
-rw-r--r--compiler/rustc_const_eval/src/interpret/intrinsics.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs14
-rw-r--r--compiler/rustc_const_eval/src/interpret/mod.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/step.rs2
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs160
-rw-r--r--compiler/rustc_const_eval/src/interpret/validity.rs2
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/marker.rs7
-rw-r--r--compiler/rustc_data_structures/src/sip128.rs11
-rw-r--r--compiler/rustc_data_structures/src/sync.rs11
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs21
-rw-r--r--compiler/rustc_error_codes/src/error_codes/E0502.md5
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs7
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs14
-rw-r--r--compiler/rustc_errors/src/emitter.rs23
-rw-r--r--compiler/rustc_errors/src/lib.rs66
-rw-r--r--compiler/rustc_expand/src/config.rs29
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs12
-rw-r--r--compiler/rustc_expand/src/mbe/macro_parser.rs2
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs74
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs32
-rw-r--r--compiler/rustc_feature/src/builtin_attrs.rs17
-rw-r--r--compiler/rustc_feature/src/unstable.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs4
-rw-r--r--compiler/rustc_hir/src/intravisit.rs3
-rw-r--r--compiler/rustc_hir/src/lang_items.rs21
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl10
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs110
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs17
-rw-r--r--compiler/rustc_hir_analysis/src/check/entry.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs123
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs194
-rw-r--r--compiler/rustc_hir_analysis/src/collect/dump.rs50
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs80
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs31
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs67
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs27
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs37
-rw-r--r--compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs (renamed from compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs)62
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs39
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs84
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs69
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs99
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/utils.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors.rs36
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs57
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs56
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs7
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl26
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs160
-rw-r--r--compiler/rustc_hir_typeck/src/closure.rs106
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs19
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs21
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs39
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs56
-rw-r--r--compiler/rustc_hir_typeck/src/expr_use_visitor.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs68
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs15
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs127
-rw-r--r--compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs27
-rw-r--r--compiler/rustc_index/src/lib.rs5
-rw-r--r--compiler/rustc_index/src/vec.rs5
-rw-r--r--compiler/rustc_index_macros/src/lib.rs5
-rw-r--r--compiler/rustc_index_macros/src/newtype.rs4
-rw-r--r--compiler/rustc_infer/Cargo.toml2
-rw-r--r--compiler/rustc_infer/src/infer/context.rs176
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs864
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs421
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/region.rs1259
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs48
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/outlives/components.rs266
-rw-r--r--compiler/rustc_infer/src/infer/outlives/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs33
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs36
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lattice.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs2
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs4
-rw-r--r--compiler/rustc_infer/src/infer/relate/type_relating.rs4
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs8
-rw-r--r--compiler/rustc_infer/src/traits/util.rs382
-rw-r--r--compiler/rustc_interface/src/lib.rs2
-rw-r--r--compiler/rustc_interface/src/queries.rs61
-rw-r--r--compiler/rustc_interface/src/tests.rs29
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/async_fn_in_trait.rs2
-rw-r--r--compiler/rustc_lint/src/builtin.rs82
-rw-r--r--compiler/rustc_lint/src/context/diagnostics/check_cfg.rs18
-rw-r--r--compiler/rustc_lint/src/internal.rs4
-rw-r--r--compiler/rustc_lint/src/lib.rs8
-rw-r--r--compiler/rustc_lint/src/lints.rs13
-rw-r--r--compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs155
-rw-r--r--compiler/rustc_lint/src/multiple_supertrait_upcastable.rs2
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs6
-rw-r--r--compiler/rustc_lint/src/noop_method_call.rs4
-rw-r--r--compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs4
-rw-r--r--compiler/rustc_lint/src/unused.rs4
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs57
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp7
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs5
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs7
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs14
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs8
-rw-r--r--compiler/rustc_middle/messages.ftl6
-rw-r--r--compiler/rustc_middle/src/error.rs14
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs34
-rw-r--r--compiler/rustc_middle/src/middle/codegen_fn_attrs.rs27
-rw-r--r--compiler/rustc_middle/src/middle/dependency_format.rs6
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs4
-rw-r--r--compiler/rustc_middle/src/middle/limits.rs2
-rw-r--r--compiler/rustc_middle/src/mir/coverage.rs11
-rw-r--r--compiler/rustc_middle/src/mir/interpret/mod.rs73
-rw-r--r--compiler/rustc_middle/src/mir/interpret/queries.rs4
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs13
-rw-r--r--compiler/rustc_middle/src/mir/pretty.rs44
-rw-r--r--compiler/rustc_middle/src/mir/query.rs3
-rw-r--r--compiler/rustc_middle/src/mir/syntax.rs31
-rw-r--r--compiler/rustc_middle/src/mir/terminator.rs13
-rw-r--r--compiler/rustc_middle/src/mir/visit.rs11
-rw-r--r--compiler/rustc_middle/src/query/mod.rs44
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs1
-rw-r--r--compiler/rustc_middle/src/traits/util.rs62
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs4
-rw-r--r--compiler/rustc_middle/src/ty/assoc.rs2
-rw-r--r--compiler/rustc_middle/src/ty/closure.rs2
-rw-r--r--compiler/rustc_middle/src/ty/consts/kind.rs7
-rw-r--r--compiler/rustc_middle/src/ty/context.rs183
-rw-r--r--compiler/rustc_middle/src/ty/elaborate_impl.rs84
-rw-r--r--compiler/rustc_middle/src/ty/fast_reject.rs368
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs5
-rw-r--r--compiler/rustc_middle/src/ty/impls_ty.rs13
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs248
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs20
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs18
-rw-r--r--compiler/rustc_middle/src/ty/print/mod.rs32
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs53
-rw-r--r--compiler/rustc_middle/src/ty/relate.rs12
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs14
-rw-r--r--compiler/rustc_middle/src/ty/trait_def.rs10
-rw-r--r--compiler/rustc_middle/src/ty/vtable.rs35
-rw-r--r--compiler/rustc_middle/src/ty/walk.rs17
-rw-r--r--compiler/rustc_middle/src/util/call_kind.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs78
-rw-r--r--compiler/rustc_mir_build/src/build/coverageinfo.rs107
-rw-r--r--compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs12
-rw-r--r--compiler/rustc_mir_build/src/build/custom/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs2
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/expr/stmt.rs39
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs539
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs77
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/scope.rs86
-rw-r--r--compiler/rustc_mir_build/src/lints.rs18
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/lattice.rs14
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs1
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/initialized.rs54
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs2
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs6
-rw-r--r--compiler/rustc_mir_dataflow/src/value_analysis.rs196
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs49
-rw-r--r--compiler/rustc_mir_transform/src/cost_checker.rs36
-rw-r--r--compiler/rustc_mir_transform/src/coverage/graph.rs9
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs34
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs81
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs12
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs100
-rw-r--r--compiler/rustc_mir_transform/src/coverage/unexpand.rs60
-rw-r--r--compiler/rustc_mir_transform/src/dataflow_const_prop.rs5
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs6
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs46
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs26
-rw-r--r--compiler/rustc_mir_transform/src/inline.rs79
-rw-r--r--compiler/rustc_mir_transform/src/inline/cycle.rs2
-rw-r--r--compiler/rustc_mir_transform/src/jump_threading.rs111
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs1
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--compiler/rustc_mir_transform/src/mentioned_items.rs2
-rw-r--r--compiler/rustc_mir_transform/src/promote_consts.rs2
-rw-r--r--compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs1
-rw-r--r--compiler/rustc_mir_transform/src/shim.rs49
-rw-r--r--compiler/rustc_mir_transform/src/validate.rs77
-rw-r--r--compiler/rustc_monomorphize/messages.ftl5
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs90
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs13
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/coherence.rs469
-rw-r--r--compiler/rustc_next_trait_solver/src/delegate.rs111
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/relate.rs15
-rw-r--r--compiler/rustc_next_trait_solver/src/relate/combine.rs34
-rw-r--r--compiler/rustc_next_trait_solver/src/resolve.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs123
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs7
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs65
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs52
-rw-r--r--compiler/rustc_parse/messages.ftl4
-rw-r--r--compiler/rustc_parse/src/errors.rs8
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs162
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs7
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs23
-rw-r--r--compiler/rustc_parse/src/parser/path.rs27
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs98
-rw-r--r--compiler/rustc_passes/messages.ftl1
-rw-r--r--compiler/rustc_passes/src/abi_test.rs2
-rw-r--r--compiler/rustc_passes/src/check_attr.rs12
-rw-r--r--compiler/rustc_passes/src/dead.rs42
-rw-r--r--compiler/rustc_passes/src/errors.rs3
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs2
-rw-r--r--compiler/rustc_passes/src/layout_test.rs2
-rw-r--r--compiler/rustc_passes/src/reachable.rs14
-rw-r--r--compiler/rustc_resolve/Cargo.toml2
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs10
-rw-r--r--compiler/rustc_resolve/src/def_collector.rs25
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs18
-rw-r--r--compiler/rustc_resolve/src/late.rs3
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs44
-rw-r--r--compiler/rustc_resolve/src/lib.rs24
-rw-r--r--compiler/rustc_resolve/src/macros.rs9
-rw-r--r--compiler/rustc_resolve/src/rustdoc.rs14
-rw-r--r--compiler/rustc_serialize/src/opaque.rs2
-rw-r--r--compiler/rustc_session/src/config.rs35
-rw-r--r--compiler/rustc_session/src/options.rs35
-rw-r--r--compiler/rustc_smir/Cargo.toml2
-rw-r--r--compiler/rustc_smir/src/rustc_smir/context.rs48
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/mir.rs3
-rw-r--r--compiler/rustc_smir/src/rustc_smir/convert/ty.rs2
-rw-r--r--compiler/rustc_span/src/symbol.rs14
-rw-r--r--compiler/rustc_symbol_mangling/src/legacy.rs10
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs11
-rw-r--r--compiler/rustc_target/src/abi/call/mod.rs8
-rw-r--r--compiler/rustc_trait_selection/Cargo.toml4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs698
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs (renamed from compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs)1240
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs (renamed from compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs)6
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs385
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs (renamed from compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs)11
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs197
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs (renamed from compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs)193
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs11
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve/delegate.rs189
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs454
-rw-r--r--compiler/rustc_trait_selection/src/traits/engine.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs136
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs182
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs61
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/vtable.rs11
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs14
-rw-r--r--compiler/rustc_traits/src/codegen.rs2
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs2
-rw-r--r--compiler/rustc_ty_utils/src/abi.rs4
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs142
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs4
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs6
-rw-r--r--compiler/rustc_ty_utils/src/sig_types.rs2
-rw-r--r--compiler/rustc_type_ir/src/binder.rs6
-rw-r--r--compiler/rustc_type_ir/src/effects.rs59
-rw-r--r--compiler/rustc_type_ir/src/elaborate.rs305
-rw-r--r--compiler/rustc_type_ir/src/fast_reject.rs397
-rw-r--r--compiler/rustc_type_ir/src/infer_ctxt.rs98
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs57
-rw-r--r--compiler/rustc_type_ir/src/interner.rs36
-rw-r--r--compiler/rustc_type_ir/src/lang_items.rs11
-rw-r--r--compiler/rustc_type_ir/src/lib.rs9
-rw-r--r--compiler/rustc_type_ir/src/outlives.rs228
-rw-r--r--compiler/stable_mir/src/compiler_interface.rs10
-rw-r--r--compiler/stable_mir/src/crate_def.rs37
431 files changed, 12201 insertions, 9245 deletions
diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs
index 9165b4f2df3..197dd7f9c9e 100644
--- a/compiler/rustc_abi/src/layout.rs
+++ b/compiler/rustc_abi/src/layout.rs
@@ -186,7 +186,7 @@ pub trait LayoutCalculator {
         let (present_first, present_second) = {
             let mut present_variants = variants
                 .iter_enumerated()
-                .filter_map(|(i, v)| if absent(v) { None } else { Some(i) });
+                .filter_map(|(i, v)| if !repr.c() && absent(v) { None } else { Some(i) });
             (present_variants.next(), present_variants.next())
         };
         let present_first = match present_first {
@@ -621,7 +621,7 @@ where
     let discr_type = repr.discr_type();
     let bits = Integer::from_attr(dl, discr_type).size().bits();
     for (i, mut val) in discriminants {
-        if variants[i].iter().any(|f| f.abi.is_uninhabited()) {
+        if !repr.c() && variants[i].iter().any(|f| f.abi.is_uninhabited()) {
             continue;
         }
         if discr_type.is_signed() {
diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs
index 31c66a56bea..78332d66f03 100644
--- a/compiler/rustc_abi/src/lib.rs
+++ b/compiler/rustc_abi/src/lib.rs
@@ -1429,7 +1429,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
     /// Single enum variants, structs/tuples, unions, and all non-ADTs.
     Single { index: VariantIdx },
 
-    /// Enum-likes with more than one inhabited variant: each variant comes with
+    /// Enum-likes with more than one variant: each variant comes with
     /// a *discriminant* (usually the same as the variant index but the user can
     /// assign explicit discriminant values). That discriminant is encoded
     /// as a *tag* on the machine. The layout of each variant is
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 4a3ce0e0c30..75c656973f9 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -176,6 +176,8 @@ pub enum GenericArgs {
     AngleBracketed(AngleBracketedArgs),
     /// The `(A, B)` and `C` in `Foo(A, B) -> C`.
     Parenthesized(ParenthesizedArgs),
+    /// `(..)` in return type notation.
+    ParenthesizedElided(Span),
 }
 
 impl GenericArgs {
@@ -187,6 +189,7 @@ impl GenericArgs {
         match self {
             AngleBracketed(data) => data.span,
             Parenthesized(data) => data.span,
+            ParenthesizedElided(span) => *span,
         }
     }
 }
@@ -194,11 +197,11 @@ impl GenericArgs {
 /// Concrete argument in the sequence of generic args.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum GenericArg {
-    /// `'a` in `Foo<'a>`
+    /// `'a` in `Foo<'a>`.
     Lifetime(Lifetime),
-    /// `Bar` in `Foo<Bar>`
+    /// `Bar` in `Foo<Bar>`.
     Type(P<Ty>),
-    /// `1` in `Foo<1>`
+    /// `1` in `Foo<1>`.
     Const(AnonConst),
 }
 
@@ -352,7 +355,7 @@ pub enum GenericParamKind {
         ty: P<Ty>,
         /// Span of the `const` keyword.
         kw_span: Span,
-        /// Optional default value for the const generic param
+        /// Optional default value for the const generic param.
         default: Option<AnonConst>,
     },
 }
@@ -711,6 +714,7 @@ pub enum ByRef {
 }
 
 impl ByRef {
+    #[must_use]
     pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
         if let ByRef::Yes(old_mutbl) = &mut self {
             *old_mutbl = cmp::min(*old_mutbl, mutbl);
@@ -829,7 +833,7 @@ pub enum PatKind {
     /// only one rest pattern may occur in the pattern sequences.
     Rest,
 
-    // A never pattern `!`
+    // A never pattern `!`.
     Never,
 
     /// Parentheses in patterns used for grouping (i.e., `(PAT)`).
@@ -1118,9 +1122,9 @@ impl LocalKind {
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct Arm {
     pub attrs: AttrVec,
-    /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`
+    /// Match arm pattern, e.g. `10` in `match foo { 10 => {}, _ => {} }`.
     pub pat: P<Pat>,
-    /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`
+    /// Match arm guard, e.g. `n > 10` in `match foo { n if n > 10 => {}, _ => {} }`.
     pub guard: Option<P<Expr>>,
     /// Match arm body. Omitted if the pattern is a never pattern.
     pub body: Option<P<Expr>>,
@@ -1351,12 +1355,12 @@ pub struct Closure {
     pub fn_arg_span: Span,
 }
 
-/// Limit types of a range (inclusive or exclusive)
+/// Limit types of a range (inclusive or exclusive).
 #[derive(Copy, Clone, PartialEq, Encodable, Decodable, Debug)]
 pub enum RangeLimits {
-    /// Inclusive at the beginning, exclusive at the end
+    /// Inclusive at the beginning, exclusive at the end.
     HalfOpen,
-    /// Inclusive at the beginning and end
+    /// Inclusive at the beginning and end.
     Closed,
 }
 
@@ -1397,9 +1401,9 @@ pub struct StructExpr {
 pub enum ExprKind {
     /// An array (e.g, `[a, b, c, d]`).
     Array(ThinVec<P<Expr>>),
-    /// Allow anonymous constants from an inline `const` block
+    /// Allow anonymous constants from an inline `const` block.
     ConstBlock(AnonConst),
-    /// A function call
+    /// A function call.
     ///
     /// The first field resolves to the function itself,
     /// and the second field is the list of arguments.
@@ -1453,8 +1457,11 @@ pub enum ExprKind {
     /// A block (`'label: { ... }`).
     Block(P<Block>, Option<Label>),
     /// An `async` block (`async move { ... }`),
-    /// or a `gen` block (`gen move { ... }`)
-    Gen(CaptureBy, P<Block>, GenBlockKind),
+    /// or a `gen` block (`gen move { ... }`).
+    ///
+    /// The span is the "decl", which is the header before the body `{ }`
+    /// including the `asyng`/`gen` keywords and possibly `move`.
+    Gen(CaptureBy, P<Block>, GenBlockKind, Span),
     /// An await expression (`my_future.await`). Span is of await keyword.
     Await(P<Expr>, Span),
 
@@ -2048,7 +2055,7 @@ impl UintTy {
 /// * the `A: Bound` in `Trait<A: Bound>`
 /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy`
 /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`)
-/// * the `f(): Bound` in `Trait<f(): Bound>` (feature `return_type_notation`)
+/// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`)
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct AssocItemConstraint {
     pub id: NodeId,
@@ -2150,9 +2157,9 @@ pub enum TyKind {
     Never,
     /// A tuple (`(A, B, C, D,...)`).
     Tup(ThinVec<P<Ty>>),
-    /// An anonymous struct type i.e. `struct { foo: Type }`
+    /// An anonymous struct type i.e. `struct { foo: Type }`.
     AnonStruct(NodeId, ThinVec<FieldDef>),
-    /// An anonymous union type i.e. `union { bar: Type }`
+    /// An anonymous union type i.e. `union { bar: Type }`.
     AnonUnion(NodeId, ThinVec<FieldDef>),
     /// A path (`module::module::...::Type`), optionally
     /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
@@ -2226,9 +2233,9 @@ pub enum TraitObjectSyntax {
 
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum PreciseCapturingArg {
-    /// Lifetime parameter
+    /// Lifetime parameter.
     Lifetime(Lifetime),
-    /// Type or const parameter
+    /// Type or const parameter.
     Arg(Path, NodeId),
 }
 
@@ -2522,11 +2529,11 @@ pub enum Safety {
 /// Iterator`.
 #[derive(Copy, Clone, Encodable, Decodable, Debug)]
 pub enum CoroutineKind {
-    /// `async`, which returns an `impl Future`
+    /// `async`, which returns an `impl Future`.
     Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
-    /// `gen`, which returns an `impl Iterator`
+    /// `gen`, which returns an `impl Iterator`.
     Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
-    /// `async gen`, which returns an `impl AsyncIterator`
+    /// `async gen`, which returns an `impl AsyncIterator`.
     AsyncGen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
 }
 
@@ -2743,7 +2750,7 @@ pub struct Variant {
     pub data: VariantData,
     /// Explicit discriminant, e.g., `Foo = 1`.
     pub disr_expr: Option<AnonConst>,
-    /// Is a macro placeholder
+    /// Is a macro placeholder.
     pub is_placeholder: bool,
 }
 
@@ -3017,19 +3024,19 @@ impl Item {
 /// `extern` qualifier on a function item or function type.
 #[derive(Clone, Copy, Encodable, Decodable, Debug)]
 pub enum Extern {
-    /// No explicit extern keyword was used
+    /// No explicit extern keyword was used.
     ///
-    /// E.g. `fn foo() {}`
+    /// E.g. `fn foo() {}`.
     None,
-    /// An explicit extern keyword was used, but with implicit ABI
+    /// An explicit extern keyword was used, but with implicit ABI.
     ///
-    /// E.g. `extern fn foo() {}`
+    /// E.g. `extern fn foo() {}`.
     ///
-    /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`)
+    /// This is just `extern "C"` (see `rustc_target::spec::abi::Abi::FALLBACK`).
     Implicit(Span),
-    /// An explicit extern keyword was used with an explicit ABI
+    /// An explicit extern keyword was used with an explicit ABI.
     ///
-    /// E.g. `extern "C" fn foo() {}`
+    /// E.g. `extern "C" fn foo() {}`.
     Explicit(StrLit, Span),
 }
 
@@ -3048,13 +3055,13 @@ impl Extern {
 /// included in this struct (e.g., `async unsafe fn` or `const extern "C" fn`).
 #[derive(Clone, Copy, Encodable, Decodable, Debug)]
 pub struct FnHeader {
-    /// Whether this is `unsafe`, or has a default safety
+    /// Whether this is `unsafe`, or has a default safety.
     pub safety: Safety,
     /// Whether this is `async`, `gen`, or nothing.
     pub coroutine_kind: Option<CoroutineKind>,
     /// The `const` keyword, if any
     pub constness: Const,
-    /// The `extern` keyword and corresponding ABI string, if any
+    /// The `extern` keyword and corresponding ABI string, if any.
     pub ext: Extern,
 }
 
@@ -3248,7 +3255,7 @@ pub enum ItemKind {
     ///
     /// E.g., `trait Foo { .. }`, `trait Foo<T> { .. }` or `auto trait Foo {}`.
     Trait(Box<Trait>),
-    /// Trait alias
+    /// Trait alias.
     ///
     /// E.g., `trait Foo = Bar + Quux;`.
     TraitAlias(Generics, GenericBounds),
diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs
index 2cf811e9122..7754ca0a0f5 100644
--- a/compiler/rustc_ast/src/ast_traits.rs
+++ b/compiler/rustc_ast/src/ast_traits.rs
@@ -10,8 +10,6 @@ use crate::{AssocItem, Expr, ForeignItem, Item, NodeId};
 use crate::{AttrItem, AttrKind, Block, Pat, Path, Ty, Visibility};
 use crate::{AttrVec, Attribute, Stmt, StmtKind};
 
-use rustc_span::Span;
-
 use std::fmt;
 use std::marker::PhantomData;
 
@@ -91,37 +89,6 @@ impl<T: AstDeref<Target: HasNodeId>> HasNodeId for T {
     }
 }
 
-/// A trait for AST nodes having a span.
-pub trait HasSpan {
-    fn span(&self) -> Span;
-}
-
-macro_rules! impl_has_span {
-    ($($T:ty),+ $(,)?) => {
-        $(
-            impl HasSpan for $T {
-                fn span(&self) -> Span {
-                    self.span
-                }
-            }
-        )+
-    };
-}
-
-impl_has_span!(AssocItem, Block, Expr, ForeignItem, Item, Pat, Path, Stmt, Ty, Visibility);
-
-impl<T: AstDeref<Target: HasSpan>> HasSpan for T {
-    fn span(&self) -> Span {
-        self.ast_deref().span()
-    }
-}
-
-impl HasSpan for AttrItem {
-    fn span(&self) -> Span {
-        self.span()
-    }
-}
-
 /// A trait for AST nodes having (or not having) collected tokens.
 pub trait HasTokens {
     fn tokens(&self) -> Option<&LazyAttrTokenStream>;
diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs
index 593c78df3cd..088ae9ba441 100644
--- a/compiler/rustc_ast/src/attr/mod.rs
+++ b/compiler/rustc_ast/src/attr/mod.rs
@@ -202,14 +202,17 @@ impl Attribute {
         }
     }
 
-    pub fn tokens(&self) -> TokenStream {
+    // Named `get_tokens` to distinguish it from the `<Attribute as HasTokens>::tokens` method.
+    pub fn get_tokens(&self) -> TokenStream {
         match &self.kind {
-            AttrKind::Normal(normal) => normal
-                .tokens
-                .as_ref()
-                .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
-                .to_attr_token_stream()
-                .to_tokenstream(),
+            AttrKind::Normal(normal) => TokenStream::new(
+                normal
+                    .tokens
+                    .as_ref()
+                    .unwrap_or_else(|| panic!("attribute is missing tokens: {self:?}"))
+                    .to_attr_token_stream()
+                    .to_token_trees(),
+            ),
             &AttrKind::DocComment(comment_kind, data) => TokenStream::token_alone(
                 token::DocComment(comment_kind, self.style, data),
                 self.span,
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 7ca950e50e6..846a108091f 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -44,7 +44,7 @@ pub mod tokenstream;
 pub mod visit;
 
 pub use self::ast::*;
-pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasSpan, HasTokens};
+pub use self::ast_traits::{AstDeref, AstNodeWrapper, HasAttrs, HasNodeId, HasTokens};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index a81c335aa23..cbf21317f1a 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -582,6 +582,7 @@ fn noop_visit_generic_args<T: MutVisitor>(generic_args: &mut GenericArgs, vis: &
     match generic_args {
         GenericArgs::AngleBracketed(data) => vis.visit_angle_bracketed_parameter_data(data),
         GenericArgs::Parenthesized(data) => vis.visit_parenthesized_parameter_data(data),
+        GenericArgs::ParenthesizedElided(span) => vis.visit_span(span),
     }
 }
 
@@ -703,7 +704,7 @@ fn visit_attr_tt<T: MutVisitor>(tt: &mut AttrTokenTree, vis: &mut T) {
             visit_attr_tts(tts, vis);
             visit_delim_span(dspan, vis);
         }
-        AttrTokenTree::Attributes(AttributesData { attrs, tokens }) => {
+        AttrTokenTree::AttrsTarget(AttrsTarget { attrs, tokens }) => {
             visit_attrs(attrs, vis);
             visit_lazy_tts_opt_mut(Some(tokens), vis);
         }
@@ -1528,8 +1529,9 @@ pub fn noop_visit_expr<T: MutVisitor>(
             visit_opt(label, |label| vis.visit_label(label));
             vis.visit_block(blk);
         }
-        ExprKind::Gen(_capture_by, body, _kind) => {
+        ExprKind::Gen(_capture_by, body, _kind, decl_span) => {
             vis.visit_block(body);
+            vis.visit_span(decl_span);
         }
         ExprKind::Await(expr, await_kw_span) => {
             vis.visit_expr(expr);
diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs
index b4ddbe20689..ee068f19332 100644
--- a/compiler/rustc_ast/src/tokenstream.rs
+++ b/compiler/rustc_ast/src/tokenstream.rs
@@ -14,7 +14,7 @@
 //! ownership of the original.
 
 use crate::ast::{AttrStyle, StmtKind};
-use crate::ast_traits::{HasAttrs, HasSpan, HasTokens};
+use crate::ast_traits::{HasAttrs, HasTokens};
 use crate::token::{self, Delimiter, Nonterminal, Token, TokenKind};
 use crate::AttrVec;
 
@@ -23,7 +23,6 @@ use rustc_data_structures::sync::{self, Lrc};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 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};
@@ -171,8 +170,8 @@ pub enum AttrTokenTree {
     Delimited(DelimSpan, DelimSpacing, Delimiter, AttrTokenStream),
     /// Stores the attributes for an attribute target,
     /// along with the tokens for that attribute target.
-    /// See `AttributesData` for more information
-    Attributes(AttributesData),
+    /// See `AttrsTarget` for more information
+    AttrsTarget(AttrsTarget),
 }
 
 impl AttrTokenStream {
@@ -180,42 +179,33 @@ impl AttrTokenStream {
         AttrTokenStream(Lrc::new(tokens))
     }
 
-    /// Converts this `AttrTokenStream` to a plain `TokenStream`.
-    /// During conversion, `AttrTokenTree::Attributes` get 'flattened'
+    /// Converts this `AttrTokenStream` to a plain `Vec<TokenTree>`.
+    /// During conversion, `AttrTokenTree::AttrsTarget` get 'flattened'
     /// back to a `TokenStream` of the form `outer_attr attr_target`.
     /// If there are inner attributes, they are inserted into the proper
     /// place in the attribute target tokens.
-    pub fn to_tokenstream(&self) -> TokenStream {
-        let trees: Vec<_> = self
-            .0
-            .iter()
-            .flat_map(|tree| match &tree {
+    pub fn to_token_trees(&self) -> Vec<TokenTree> {
+        let mut res = Vec::with_capacity(self.0.len());
+        for tree in self.0.iter() {
+            match tree {
                 AttrTokenTree::Token(inner, spacing) => {
-                    smallvec![TokenTree::Token(inner.clone(), *spacing)].into_iter()
+                    res.push(TokenTree::Token(inner.clone(), *spacing));
                 }
                 AttrTokenTree::Delimited(span, spacing, delim, stream) => {
-                    smallvec![TokenTree::Delimited(
+                    res.push(TokenTree::Delimited(
                         *span,
                         *spacing,
                         *delim,
-                        stream.to_tokenstream()
-                    ),]
-                    .into_iter()
+                        TokenStream::new(stream.to_token_trees()),
+                    ))
                 }
-                AttrTokenTree::Attributes(data) => {
-                    let idx = data
+                AttrTokenTree::AttrsTarget(target) => {
+                    let idx = target
                         .attrs
                         .partition_point(|attr| matches!(attr.style, crate::AttrStyle::Outer));
-                    let (outer_attrs, inner_attrs) = data.attrs.split_at(idx);
-
-                    let mut target_tokens: Vec<_> = data
-                        .tokens
-                        .to_attr_token_stream()
-                        .to_tokenstream()
-                        .0
-                        .iter()
-                        .cloned()
-                        .collect();
+                    let (outer_attrs, inner_attrs) = target.attrs.split_at(idx);
+
+                    let mut target_tokens = target.tokens.to_attr_token_stream().to_token_trees();
                     if !inner_attrs.is_empty() {
                         let mut found = false;
                         // Check the last two trees (to account for a trailing semi)
@@ -237,7 +227,7 @@ impl AttrTokenStream {
 
                                 let mut stream = TokenStream::default();
                                 for inner_attr in inner_attrs {
-                                    stream.push_stream(inner_attr.tokens());
+                                    stream.push_stream(inner_attr.get_tokens());
                                 }
                                 stream.push_stream(delim_tokens.clone());
                                 *tree = TokenTree::Delimited(*span, *spacing, *delim, stream);
@@ -251,17 +241,14 @@ impl AttrTokenStream {
                             "Failed to find trailing delimited group in: {target_tokens:?}"
                         );
                     }
-                    let mut flat: SmallVec<[_; 1]> =
-                        SmallVec::with_capacity(target_tokens.len() + outer_attrs.len());
                     for attr in outer_attrs {
-                        flat.extend(attr.tokens().0.iter().cloned());
+                        res.extend(attr.get_tokens().0.iter().cloned());
                     }
-                    flat.extend(target_tokens);
-                    flat.into_iter()
+                    res.extend(target_tokens);
                 }
-            })
-            .collect();
-        TokenStream::new(trees)
+            }
+        }
+        res
     }
 }
 
@@ -275,7 +262,7 @@ impl AttrTokenStream {
 /// have an `attrs` field containing the `#[cfg(FALSE)]` attr,
 /// and a `tokens` field storing the (unparsed) tokens `struct Foo {}`
 #[derive(Clone, Debug, Encodable, Decodable)]
-pub struct AttributesData {
+pub struct AttrsTarget {
     /// Attributes, both outer and inner.
     /// These are stored in the original order that they were parsed in.
     pub attrs: AttrVec,
@@ -409,8 +396,8 @@ impl PartialEq<TokenStream> for TokenStream {
 }
 
 impl TokenStream {
-    pub fn new(streams: Vec<TokenTree>) -> TokenStream {
-        TokenStream(Lrc::new(streams))
+    pub fn new(tts: Vec<TokenTree>) -> TokenStream {
+        TokenStream(Lrc::new(tts))
     }
 
     pub fn is_empty(&self) -> bool {
@@ -449,19 +436,19 @@ impl TokenStream {
         TokenStream::new(vec![TokenTree::token_alone(kind, span)])
     }
 
-    pub fn from_ast(node: &(impl HasAttrs + HasSpan + HasTokens + fmt::Debug)) -> TokenStream {
+    pub fn from_ast(node: &(impl HasAttrs + HasTokens + fmt::Debug)) -> TokenStream {
         let Some(tokens) = node.tokens() else {
-            panic!("missing tokens for node at {:?}: {:?}", node.span(), node);
+            panic!("missing tokens for node: {:?}", node);
         };
         let attrs = node.attrs();
         let attr_stream = if attrs.is_empty() {
             tokens.to_attr_token_stream()
         } else {
-            let attr_data =
-                AttributesData { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() };
-            AttrTokenStream::new(vec![AttrTokenTree::Attributes(attr_data)])
+            let target =
+                AttrsTarget { attrs: attrs.iter().cloned().collect(), tokens: tokens.clone() };
+            AttrTokenStream::new(vec![AttrTokenTree::AttrsTarget(target)])
         };
-        attr_stream.to_tokenstream()
+        TokenStream::new(attr_stream.to_token_trees())
     }
 
     pub fn from_nonterminal_ast(nt: &Nonterminal) -> TokenStream {
@@ -778,6 +765,7 @@ mod size_asserts {
     static_assert_size!(AttrTokenStream, 8);
     static_assert_size!(AttrTokenTree, 32);
     static_assert_size!(LazyAttrTokenStream, 8);
+    static_assert_size!(Option<LazyAttrTokenStream>, 8); // must be small, used in many AST nodes
     static_assert_size!(TokenStream, 8);
     static_assert_size!(TokenTree, 32);
     // tidy-alphabetical-end
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 4b2544ac47e..1a80a9ccdbf 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -1,7 +1,8 @@
 //! Routines the parser and pretty-printer use to classify AST nodes.
 
 use crate::ast::ExprKind::*;
-use crate::{ast, token::Delimiter};
+use crate::ast::{self, MatchKind};
+use crate::token::Delimiter;
 
 /// This classification determines whether various syntactic positions break out
 /// of parsing the current expression (true) or continue parsing more of the
@@ -81,6 +82,82 @@ pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
     }
 }
 
+/// Returns whether the leftmost token of the given expression is the label of a
+/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`.
+///
+/// Such expressions are not allowed as the value of an unlabeled break.
+///
+/// ```ignore (illustrative)
+/// 'outer: {
+///     break 'inner: loop { break 'inner 1 } + 1;  // invalid syntax
+///
+///     break 'outer 'inner: loop { break 'inner 1 } + 1;  // okay
+///
+///     break ('inner: loop { break 'inner 1 } + 1);  // okay
+///
+///     break ('inner: loop { break 'inner 1 }) + 1;  // okay
+/// }
+/// ```
+pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
+    loop {
+        match &expr.kind {
+            Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => {
+                return label.is_some();
+            }
+
+            Assign(e, _, _)
+            | AssignOp(_, e, _)
+            | Await(e, _)
+            | Binary(_, e, _)
+            | Call(e, _)
+            | Cast(e, _)
+            | Field(e, _)
+            | Index(e, _, _)
+            | Match(e, _, MatchKind::Postfix)
+            | Range(Some(e), _, _)
+            | Try(e) => {
+                expr = e;
+            }
+            MethodCall(method_call) => {
+                expr = &method_call.receiver;
+            }
+
+            AddrOf(..)
+            | Array(..)
+            | Become(..)
+            | Break(..)
+            | Closure(..)
+            | ConstBlock(..)
+            | Continue(..)
+            | FormatArgs(..)
+            | Gen(..)
+            | If(..)
+            | IncludedBytes(..)
+            | InlineAsm(..)
+            | Let(..)
+            | Lit(..)
+            | MacCall(..)
+            | Match(_, _, MatchKind::Prefix)
+            | OffsetOf(..)
+            | Paren(..)
+            | Path(..)
+            | Range(None, _, _)
+            | Repeat(..)
+            | Ret(..)
+            | Struct(..)
+            | TryBlock(..)
+            | Tup(..)
+            | Type(..)
+            | Unary(..)
+            | Underscore
+            | Yeet(..)
+            | Yield(..)
+            | Err(..)
+            | Dummy => return false,
+        }
+    }
+}
+
 pub enum TrailingBrace<'a> {
     /// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
     /// We will suggest changing the macro call to a different delimiter.
@@ -234,6 +311,6 @@ fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
             ast::FnRetTy::Default(_) => None,
             ast::FnRetTy::Ty(ret) => Some(ret),
         },
-        ast::GenericArgs::AngleBracketed(_) => None,
+        ast::GenericArgs::AngleBracketed(_) | ast::GenericArgs::ParenthesizedElided(_) => None,
     }
 }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 0f3c30048ce..f6929057bed 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -609,6 +609,7 @@ where
             walk_list!(visitor, visit_ty, inputs);
             try_visit!(visitor.visit_fn_ret_ty(output));
         }
+        GenericArgs::ParenthesizedElided(_span) => {}
     }
     V::Result::output()
 }
@@ -1122,7 +1123,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) -> V
             visit_opt!(visitor, visit_label, opt_label);
             try_visit!(visitor.visit_block(block));
         }
-        ExprKind::Gen(_capt, body, _kind) => try_visit!(visitor.visit_block(body)),
+        ExprKind::Gen(_capt, body, _kind, _decl_span) => try_visit!(visitor.visit_block(body)),
         ExprKind::Await(expr, _span) => try_visit!(visitor.visit_expr(expr)),
         ExprKind::Assign(lhs, rhs, _span) => {
             try_visit!(visitor.visit_expr(lhs));
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 58f65f1257f..9ed93d481e7 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -36,10 +36,15 @@ ast_lowering_bad_return_type_notation_inputs =
     argument types not allowed with return type notation
     .suggestion = remove the input types
 
+ast_lowering_bad_return_type_notation_needs_dots = return type notation arguments must be elided with `..`
+    .suggestion = add `..`
+
 ast_lowering_bad_return_type_notation_output =
     return type not allowed with return type notation
     .suggestion = remove the return type
 
+ast_lowering_bad_return_type_notation_position = return type notation not allowed in this position yet
+
 ast_lowering_base_expression_double_dot =
     base expression required after `..`
     .suggestion = add a base expression here
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 3d4b6a1f033..4c77892a6b7 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -393,6 +393,17 @@ pub enum BadReturnTypeNotation {
         #[suggestion(code = "", applicability = "maybe-incorrect")]
         span: Span,
     },
+    #[diag(ast_lowering_bad_return_type_notation_needs_dots)]
+    NeedsDots {
+        #[primary_span]
+        #[suggestion(code = "(..)", applicability = "maybe-incorrect")]
+        span: Span,
+    },
+    #[diag(ast_lowering_bad_return_type_notation_position)]
+    Position {
+        #[primary_span]
+        span: Span,
+    },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 79cff0fbcd2..218fa974022 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -103,7 +103,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         ParamMode::Optional,
                         ParenthesizedGenericArgs::Err,
                         ImplTraitContext::Disallowed(ImplTraitPosition::Path),
-                        None,
                         // Method calls can't have bound modifiers
                         None,
                     ));
@@ -227,7 +226,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         *fn_arg_span,
                     ),
                 },
-                ExprKind::Gen(capture_clause, block, genblock_kind) => {
+                ExprKind::Gen(capture_clause, block, genblock_kind, decl_span) => {
                     let desugaring_kind = match genblock_kind {
                         GenBlockKind::Async => hir::CoroutineDesugaring::Async,
                         GenBlockKind::Gen => hir::CoroutineDesugaring::Gen,
@@ -237,6 +236,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         *capture_clause,
                         e.id,
                         None,
+                        *decl_span,
                         e.span,
                         desugaring_kind,
                         hir::CoroutineSource::Block,
@@ -616,6 +616,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         capture_clause: CaptureBy,
         closure_node_id: NodeId,
         return_ty: Option<hir::FnRetTy<'hir>>,
+        fn_decl_span: Span,
         span: Span,
         desugaring_kind: hir::CoroutineDesugaring,
         coroutine_source: hir::CoroutineSource,
@@ -692,7 +693,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             bound_generic_params: &[],
             fn_decl,
             body,
-            fn_decl_span: self.lower_span(span),
+            fn_decl_span: self.lower_span(fn_decl_span),
             fn_arg_span: None,
             kind: hir::ClosureKind::Coroutine(coroutine_kind),
             constness: hir::Constness::NotConst,
@@ -1083,6 +1084,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
                     &inner_decl,
                     |this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
+                    fn_decl_span,
                     body.span,
                     coroutine_kind,
                     hir::CoroutineSource::Closure,
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 4c7e8c24d32..0ad23b53566 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -56,7 +56,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> {
         owner: NodeId,
         f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>,
     ) {
-        let mut lctx = LoweringContext::new(self.tcx, self.resolver);
+        let mut lctx = LoweringContext::new(self.tcx, self.resolver, self.ast_index);
         lctx.with_hir_id_owner(owner, |lctx| f(lctx));
 
         for (def_id, info) in lctx.children {
@@ -190,6 +190,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, (ty, body_id)) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -211,6 +212,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     // declaration (decl), not the return types.
                     let coroutine_kind = header.coroutine_kind;
                     let body_id = this.lower_maybe_coroutine_body(
+                        *fn_sig_span,
                         span,
                         hir_id,
                         decl,
@@ -220,7 +222,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
 
                     let itctx = ImplTraitContext::Universal;
                     let (generics, decl) =
-                        this.lower_generics(generics, header.constness, id, itctx, |this| {
+                        this.lower_generics(generics, header.constness, false, id, itctx, |this| {
                             this.lower_fn_decl(
                                 decl,
                                 id,
@@ -264,6 +266,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, ty) = self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -292,6 +295,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, variants) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -306,6 +310,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, struct_def) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, struct_def),
@@ -316,6 +321,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, vdata) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| this.lower_variant_data(hir_id, vdata),
@@ -347,12 +353,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // parent lifetime.
                 let itctx = ImplTraitContext::Universal;
                 let (generics, (trait_ref, lowered_ty)) =
-                    self.lower_generics(ast_generics, *constness, id, itctx, |this| {
+                    self.lower_generics(ast_generics, Const::No, false, id, itctx, |this| {
                         let modifiers = TraitBoundModifiers {
-                            constness: match *constness {
-                                Const::Yes(span) => BoundConstness::Maybe(span),
-                                Const::No => BoundConstness::Never,
-                            },
+                            constness: BoundConstness::Never,
                             asyncness: BoundAsyncness::Normal,
                             // we don't use this in bound lowering
                             polarity: BoundPolarity::Positive,
@@ -388,6 +391,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(*s)),
                 };
                 hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
+                    constness: self.lower_constness(*constness),
                     safety: self.lower_safety(*safety, hir::Safety::Safe),
                     polarity,
                     defaultness,
@@ -399,15 +403,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }))
             }
             ItemKind::Trait(box Trait { is_auto, safety, generics, bounds, items }) => {
-                // FIXME(const_trait_impl, effects, fee1-dead) this should be simplified if possible
-                let constness = attrs
-                    .unwrap_or(&[])
-                    .iter()
-                    .find(|x| x.has_name(sym::const_trait))
-                    .map_or(Const::No, |x| Const::Yes(x.span));
                 let (generics, (safety, items, bounds)) = self.lower_generics(
                     generics,
-                    constness,
+                    Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -428,6 +427,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, bounds) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -608,30 +608,48 @@ 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.
 
-        let generics = match parent_hir.node().expect_item().kind {
+        let parent_item = parent_hir.node().expect_item();
+        let constness = match parent_item.kind {
             hir::ItemKind::Impl(impl_) => {
                 self.is_in_trait_impl = impl_.of_trait.is_some();
-                &impl_.generics
+                // N.B. the impl should always lower to methods that have `const host: bool` params if the trait
+                // is const. It doesn't matter whether the `impl` itself is const. Disallowing const fn from
+                // calling non-const impls are done through associated types.
+                if let Some(def_id) = impl_.of_trait.and_then(|tr| tr.trait_def_id()) {
+                    if let Some(local_def) = def_id.as_local() {
+                        match &self.ast_index[local_def] {
+                            AstOwner::Item(ast::Item { attrs, .. }) => attrs
+                                .iter()
+                                .find(|attr| attr.has_name(sym::const_trait))
+                                .map_or(Const::No, |attr| Const::Yes(attr.span)),
+                            _ => Const::No,
+                        }
+                    } else {
+                        if self.tcx.is_const_trait(def_id) {
+                            // FIXME(effects) span
+                            Const::Yes(self.tcx.def_ident_span(def_id).unwrap())
+                        } else {
+                            Const::No
+                        }
+                    }
+                } else {
+                    Const::No
+                }
             }
-            hir::ItemKind::Trait(_, _, generics, _, _) => generics,
+            hir::ItemKind::Trait(_, _, _, _, _) => parent_hir
+                .attrs
+                .get(parent_item.hir_id().local_id)
+                .iter()
+                .find(|attr| attr.has_name(sym::const_trait))
+                .map_or(Const::No, |attr| Const::Yes(attr.span)),
             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 {
-            AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item)),
-            AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item)),
+            AssocCtxt::Trait => hir::OwnerNode::TraitItem(self.lower_trait_item(item, constness)),
+            AssocCtxt::Impl => hir::OwnerNode::ImplItem(self.lower_impl_item(item, constness)),
         }
     }
 
@@ -647,7 +665,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let fdec = &sig.decl;
                     let itctx = ImplTraitContext::Universal;
                     let (generics, (fn_dec, fn_args)) =
-                        self.lower_generics(generics, Const::No, i.id, itctx, |this| {
+                        self.lower_generics(generics, Const::No, false, i.id, itctx, |this| {
                             (
                                 // Disallow `impl Trait` in foreign items.
                                 this.lower_fn_decl(
@@ -764,7 +782,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    fn lower_trait_item(&mut self, i: &AssocItem) -> &'hir hir::TraitItem<'hir> {
+    fn lower_trait_item(
+        &mut self,
+        i: &AssocItem,
+        trait_constness: Const,
+    ) -> &'hir hir::TraitItem<'hir> {
         let hir_id = self.lower_node_id(i.id);
         self.lower_attrs(hir_id, &i.attrs);
         let trait_item_def_id = hir_id.expect_owner();
@@ -774,6 +796,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, kind) = self.lower_generics(
                     generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -794,11 +817,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    trait_constness,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
             }
             AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
                 let body_id = self.lower_maybe_coroutine_body(
+                    sig.span,
                     i.span,
                     hir_id,
                     &sig.decl,
@@ -811,6 +836,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     FnDeclKind::Trait,
                     sig.header.coroutine_kind,
+                    trait_constness,
                 );
                 (generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
             }
@@ -820,6 +846,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 let (generics, kind) = self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| {
@@ -892,7 +919,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
         self.expr(span, hir::ExprKind::Err(guar))
     }
 
-    fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
+    fn lower_impl_item(
+        &mut self,
+        i: &AssocItem,
+        constness_of_trait: Const,
+    ) -> &'hir hir::ImplItem<'hir> {
         // Since `default impl` is not yet implemented, this is always true in impls.
         let has_value = true;
         let (defaultness, _) = self.lower_defaultness(i.kind.defaultness(), has_value);
@@ -903,6 +934,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             AssocItemKind::Const(box ConstItem { generics, ty, expr, .. }) => self.lower_generics(
                 generics,
                 Const::No,
+                false,
                 i.id,
                 ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                 |this| {
@@ -915,6 +947,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ),
             AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
                 let body_id = self.lower_maybe_coroutine_body(
+                    sig.span,
                     i.span,
                     hir_id,
                     &sig.decl,
@@ -927,6 +960,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     i.id,
                     if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
                     sig.header.coroutine_kind,
+                    constness_of_trait,
                 );
 
                 (generics, hir::ImplItemKind::Fn(sig, body_id))
@@ -937,6 +971,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 self.lower_generics(
                     &generics,
                     Const::No,
+                    false,
                     i.id,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
                     |this| match ty {
@@ -1111,6 +1146,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     /// `gen {}` block as appropriate.
     fn lower_maybe_coroutine_body(
         &mut self,
+        fn_decl_span: Span,
         span: Span,
         fn_id: hir::HirId,
         decl: &FnDecl,
@@ -1124,6 +1160,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
                 decl,
                 |this| this.lower_block_expr(body),
+                fn_decl_span,
                 body.span,
                 coroutine_kind,
                 hir::CoroutineSource::Fn,
@@ -1145,6 +1182,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         decl: &FnDecl,
         lower_body: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::Expr<'hir>,
+        fn_decl_span: Span,
         body_span: Span,
         coroutine_kind: CoroutineKind,
         coroutine_source: hir::CoroutineSource,
@@ -1315,13 +1353,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
         };
         let closure_id = coroutine_kind.closure_id();
 
-        let span = if let FnRetTy::Default(span) = decl.output
-            && matches!(coroutine_source, rustc_hir::CoroutineSource::Closure)
-        {
-            body_span.with_lo(span.lo())
-        } else {
-            body_span
-        };
         let coroutine_expr = self.make_desugared_coroutine_expr(
             // The default capture mode here is by-ref. Later on during upvar analysis,
             // we will force the captured arguments to by-move, but for async closures,
@@ -1330,7 +1361,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             CaptureBy::Ref,
             closure_id,
             None,
-            span,
+            fn_decl_span,
+            body_span,
             desugaring_kind,
             coroutine_source,
             mkbody,
@@ -1352,15 +1384,18 @@ impl<'hir> LoweringContext<'_, 'hir> {
         id: NodeId,
         kind: FnDeclKind,
         coroutine_kind: Option<CoroutineKind>,
+        parent_constness: Const,
     ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
         let header = self.lower_fn_header(sig.header);
         // Don't pass along the user-provided constness of trait associated functions; we don't want to
         // synthesize a host effect param for them. We reject `const` on them during AST validation.
-        let constness = if kind == FnDeclKind::Inherent { sig.header.constness } else { Const::No };
+        let constness =
+            if kind == FnDeclKind::Inherent { sig.header.constness } else { parent_constness };
         let itctx = ImplTraitContext::Universal;
-        let (generics, decl) = self.lower_generics(generics, constness, id, itctx, |this| {
-            this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
-        });
+        let (generics, decl) =
+            self.lower_generics(generics, constness, kind == FnDeclKind::Impl, id, itctx, |this| {
+                this.lower_fn_decl(&sig.decl, id, sig.span, kind, coroutine_kind)
+            });
         (generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
     }
 
@@ -1436,6 +1471,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         &mut self,
         generics: &Generics,
         constness: Const,
+        force_append_constness: bool,
         parent_node_id: NodeId,
         itctx: ImplTraitContext,
         f: impl FnOnce(&mut Self) -> T,
@@ -1496,7 +1532,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // if the effects feature is enabled. This needs to be done before we lower where
         // clauses since where clauses need to bind to the DefId of the host param
         let host_param_parts = if let Const::Yes(span) = constness
-            && self.tcx.features().effects
+            // if this comes from implementing a `const` trait, we must force constness to be appended
+            // to the impl item, no matter whether effects is enabled.
+            && (self.tcx.features().effects || force_append_constness)
         {
             let span = self.lower_span(span);
             let param_node_id = self.next_node_id();
@@ -1609,6 +1647,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             }),
                         )),
                     )),
+                    // FIXME(effects) we might not need a default.
                     default: Some(self.arena.alloc(hir::AnonConst {
                         def_id: anon_const,
                         hir_id: const_id,
@@ -1616,6 +1655,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                         span,
                     })),
                     is_host_effect: true,
+                    synthetic: true,
                 },
                 colon_span: None,
                 pure_wrt_drop: false,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0a06304fcec..24748d2d009 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -142,14 +142,19 @@ struct LoweringContext<'a, 'hir> {
     generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
 
     host_param_id: Option<LocalDefId>,
+    ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
 }
 
 impl<'a, 'hir> LoweringContext<'a, 'hir> {
-    fn new(tcx: TyCtxt<'hir>, resolver: &'a mut ResolverAstLowering) -> Self {
+    fn new(
+        tcx: TyCtxt<'hir>,
+        resolver: &'a mut ResolverAstLowering,
+        ast_index: &'a IndexSlice<LocalDefId, AstOwner<'a>>,
+    ) -> Self {
         Self {
             // Pseudo-globals.
             tcx,
-            resolver: resolver,
+            resolver,
             arena: tcx.hir_arena,
 
             // HirId handling.
@@ -185,6 +190,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             allow_async_iterator: [sym::gen_future, sym::async_iterator].into(),
             generics_def_id_map: Default::default(),
             host_param_id: None,
+            ast_index,
         }
     }
 
@@ -979,20 +985,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     self.lower_angle_bracketed_parameter_data(data, ParamMode::Explicit, itctx).0
                 }
                 GenericArgs::Parenthesized(data) => {
-                    if data.inputs.is_empty() && matches!(data.output, FnRetTy::Default(..)) {
-                        let parenthesized = if self.tcx.features().return_type_notation {
-                            hir::GenericArgsParentheses::ReturnTypeNotation
-                        } else {
-                            self.emit_bad_parenthesized_trait_in_assoc_ty(data);
-                            hir::GenericArgsParentheses::No
-                        };
-                        GenericArgsCtor {
-                            args: Default::default(),
-                            constraints: &[],
-                            parenthesized,
-                            span: data.inputs_span,
-                        }
-                    } else if let Some(first_char) = constraint.ident.as_str().chars().next()
+                    if let Some(first_char) = constraint.ident.as_str().chars().next()
                         && first_char.is_ascii_lowercase()
                     {
                         let mut err = if !data.inputs.is_empty() {
@@ -1004,7 +997,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 span: data.inputs_span.shrink_to_hi().to(ty.span),
                             })
                         } else {
-                            unreachable!("inputs are empty and return type is not provided")
+                            self.dcx().create_err(errors::BadReturnTypeNotation::NeedsDots {
+                                span: data.inputs_span,
+                            })
                         };
                         if !self.tcx.features().return_type_notation
                             && self.tcx.sess.is_nightly_build()
@@ -1034,6 +1029,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         .0
                     }
                 }
+                GenericArgs::ParenthesizedElided(span) => GenericArgsCtor {
+                    args: Default::default(),
+                    constraints: &[],
+                    parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
+                    span: *span,
+                },
             };
             gen_args_ctor.into_generic_args(self)
         } else {
@@ -2135,7 +2136,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param: &GenericParam,
         source: hir::GenericParamSource,
     ) -> hir::GenericParam<'hir> {
-        let (name, kind) = self.lower_generic_param_kind(param, source);
+        let (name, kind) = self.lower_generic_param_kind(
+            param,
+            source,
+            attr::contains_name(&param.attrs, sym::rustc_runtime),
+        );
 
         let hir_id = self.lower_node_id(param.id);
         self.lower_attrs(hir_id, &param.attrs);
@@ -2155,6 +2160,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         param: &GenericParam,
         source: hir::GenericParamSource,
+        is_host_effect: bool,
     ) -> (hir::ParamName, hir::GenericParamKind<'hir>) {
         match &param.kind {
             GenericParamKind::Lifetime => {
@@ -2220,7 +2226,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                 (
                     hir::ParamName::Plain(self.lower_ident(param.ident)),
-                    hir::GenericParamKind::Const { ty, default, is_host_effect: false },
+                    hir::GenericParamKind::Const { ty, default, is_host_effect, synthetic: false },
                 )
             }
         }
@@ -2607,78 +2613,6 @@ struct GenericArgsCtor<'hir> {
 }
 
 impl<'hir> GenericArgsCtor<'hir> {
-    fn push_constness(
-        &mut self,
-        lcx: &mut LoweringContext<'_, 'hir>,
-        constness: ast::BoundConstness,
-    ) {
-        if !lcx.tcx.features().effects {
-            return;
-        }
-
-        let (span, body) = match constness {
-            BoundConstness::Never => return,
-            BoundConstness::Always(span) => {
-                let span = lcx.lower_span(span);
-
-                let body = hir::ExprKind::Lit(
-                    lcx.arena.alloc(hir::Lit { node: LitKind::Bool(false), span }),
-                );
-
-                (span, body)
-            }
-            BoundConstness::Maybe(span) => {
-                let span = lcx.lower_span(span);
-
-                let Some(host_param_id) = lcx.host_param_id else {
-                    lcx.dcx().span_delayed_bug(
-                        span,
-                        "no host param id for call in const yet no errors reported",
-                    );
-                    return;
-                };
-
-                let hir_id = lcx.next_id();
-                let res = Res::Def(DefKind::ConstParam, host_param_id.to_def_id());
-                let body = hir::ExprKind::Path(hir::QPath::Resolved(
-                    None,
-                    lcx.arena.alloc(hir::Path {
-                        span,
-                        res,
-                        segments: arena_vec![
-                            lcx;
-                            hir::PathSegment::new(
-                                Ident { name: sym::host, span },
-                                hir_id,
-                                res
-                            )
-                        ],
-                    }),
-                ));
-
-                (span, body)
-            }
-        };
-        let body = lcx.lower_body(|lcx| (&[], lcx.expr(span, body)));
-
-        let id = lcx.next_node_id();
-        let hir_id = lcx.next_id();
-
-        let def_id = lcx.create_def(
-            lcx.current_hir_id_owner.def_id,
-            id,
-            kw::Empty,
-            DefKind::AnonConst,
-            span,
-        );
-
-        lcx.children.push((def_id, hir::MaybeOwner::NonOwner(hir_id)));
-        self.args.push(hir::GenericArg::Const(hir::ConstArg {
-            value: lcx.arena.alloc(hir::AnonConst { def_id, hir_id, body, span }),
-            is_desugared_from_effects: true,
-        }))
-    }
-
     fn is_empty(&self) -> bool {
         self.args.is_empty()
             && self.constraints.is_empty()
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 9d38e1e6784..6303584bb78 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,7 +1,8 @@
 use crate::ImplTraitPosition;
 
 use super::errors::{
-    AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, GenericTypeWithParentheses, UseAngleBrackets,
+    AsyncBoundNotOnTrait, AsyncBoundOnlyForFnTraits, BadReturnTypeNotation,
+    GenericTypeWithParentheses, UseAngleBrackets,
 };
 use super::ResolverAstLoweringExt;
 use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
@@ -107,8 +108,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         param_mode,
                         parenthesized_generic_args,
                         itctx,
-                        // if this is the last segment, add constness to the trait path
-                        if i == proj_start - 1 { modifiers.map(|m| m.constness) } else { None },
                         bound_modifier_allowed_features.clone(),
                     )
                 },
@@ -165,7 +164,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 ParenthesizedGenericArgs::Err,
                 itctx,
                 None,
-                None,
             ));
             let qpath = hir::QPath::TypeRelative(ty, hir_segment);
 
@@ -208,7 +206,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     ParenthesizedGenericArgs::Err,
                     ImplTraitContext::Disallowed(ImplTraitPosition::Path),
                     None,
-                    None,
                 )
             })),
             span: self.lower_span(p.span),
@@ -222,7 +219,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         param_mode: ParamMode,
         parenthesized_generic_args: ParenthesizedGenericArgs,
         itctx: ImplTraitContext,
-        constness: Option<ast::BoundConstness>,
         // Additional features ungated with a bound modifier like `async`.
         // This is passed down to the implicit associated type binding in
         // parenthesized bounds.
@@ -276,6 +272,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         )
                     }
                 },
+                GenericArgs::ParenthesizedElided(span) => {
+                    self.dcx().emit_err(BadReturnTypeNotation::Position { span: *span });
+                    (
+                        GenericArgsCtor {
+                            args: Default::default(),
+                            constraints: &[],
+                            parenthesized: hir::GenericArgsParentheses::ReturnTypeNotation,
+                            span: *span,
+                        },
+                        false,
+                    )
+                }
             }
         } else {
             (
@@ -289,10 +297,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             )
         };
 
-        if let Some(constness) = constness {
-            generic_args.push_constness(self, constness);
-        }
-
         let has_lifetimes =
             generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index ba4b6130b60..dd0d904c52c 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1,10 +1,20 @@
-// Validate AST before lowering it to HIR.
-//
-// This pass is supposed to catch things that fit into AST data structures,
-// but not permitted by the language. It runs after expansion when AST is frozen,
-// so it can check for erroneous constructions produced by syntax extensions.
-// This pass is supposed to perform only simple checks not requiring name resolution
-// or type checking or some other kind of complex analysis.
+//! Validate AST before lowering it to HIR.
+//!
+//! This pass intends to check that the constructed AST is *syntactically valid* to allow the rest
+//! of the compiler to assume that the AST is valid. These checks cannot be performed during parsing
+//! because attribute macros are allowed to accept certain pieces of invalid syntax such as a
+//! function without body outside of a trait definition:
+//!
+//! ```ignore (illustrative)
+//! #[my_attribute]
+//! mod foo {
+//!     fn missing_body();
+//! }
+//! ```
+//!
+//! These checks are run post-expansion, after AST is frozen, to be able to check for erroneous
+//! constructions produced by proc macros. This pass is only intended for simple checks that do not
+//! require name resolution or type checking, or other kinds of complex analysis.
 
 use itertools::{Either, Itertools};
 use rustc_ast::ptr::P;
@@ -459,13 +469,18 @@ impl<'a> AstValidator<'a> {
     fn check_item_safety(&self, span: Span, safety: Safety) {
         match self.extern_mod_safety {
             Some(extern_safety) => {
-                if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_))
-                    && (extern_safety == Safety::Default || !self.features.unsafe_extern_blocks)
-                {
-                    self.dcx().emit_err(errors::InvalidSafetyOnExtern {
-                        item_span: span,
-                        block: self.current_extern_span().shrink_to_lo(),
-                    });
+                if matches!(safety, Safety::Unsafe(_) | Safety::Safe(_)) {
+                    if extern_safety == Safety::Default {
+                        self.dcx().emit_err(errors::InvalidSafetyOnExtern {
+                            item_span: span,
+                            block: Some(self.current_extern_span().shrink_to_lo()),
+                        });
+                    } else if !self.features.unsafe_extern_blocks {
+                        self.dcx().emit_err(errors::InvalidSafetyOnExtern {
+                            item_span: span,
+                            block: None,
+                        });
+                    }
                 }
             }
             None => {
@@ -1088,7 +1103,15 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                             }
                         }
                     } else if let &Safety::Unsafe(span) = safety {
-                        this.dcx().emit_err(errors::UnsafeItem { span, kind: "extern block" });
+                        let mut diag = this
+                            .dcx()
+                            .create_err(errors::UnsafeItem { span, kind: "extern block" });
+                        rustc_session::parse::add_feature_diagnostics(
+                            &mut diag,
+                            self.session,
+                            sym::unsafe_extern_blocks,
+                        );
+                        diag.emit();
                     }
 
                     if abi.is_none() {
@@ -1289,6 +1312,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                     self.with_impl_trait(None, |this| this.visit_ty(ty));
                 }
             }
+            GenericArgs::ParenthesizedElided(_span) => {}
         }
     }
 
@@ -1445,7 +1469,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                                 span: args.span,
                             });
                         }
-                        None => {}
+                        Some(ast::GenericArgs::ParenthesizedElided(_)) | None => {}
                     }
                 }
             }
@@ -1693,7 +1717,9 @@ fn deny_equality_constraints(
                 // Add `<Bar = RhsTy>` to `Foo`.
                 match &mut assoc_path.segments[len].args {
                     Some(args) => match args.deref_mut() {
-                        GenericArgs::Parenthesized(_) => continue,
+                        GenericArgs::Parenthesized(_) | GenericArgs::ParenthesizedElided(..) => {
+                            continue;
+                        }
                         GenericArgs::AngleBracketed(args) => {
                             args.args.push(arg);
                         }
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 965d8fac712..bfb90476450 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -222,7 +222,7 @@ pub struct InvalidSafetyOnExtern {
     #[primary_span]
     pub item_span: Span,
     #[suggestion(code = "unsafe ", applicability = "machine-applicable", style = "verbose")]
-    pub block: Span,
+    pub block: Option<Span>,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index e1c1a027a30..9cf3182daea 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -1,6 +1,6 @@
 use rustc_ast as ast;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
-use rustc_ast::{attr, AssocItemConstraint, AssocItemConstraintKind, NodeId};
+use rustc_ast::{attr, NodeId};
 use rustc_ast::{token, PatKind};
 use rustc_feature::{AttributeGate, BuiltinAttribute, Features, GateIssue, BUILTIN_ATTRIBUTE_MAP};
 use rustc_session::parse::{feature_err, feature_err_issue, feature_warn};
@@ -445,23 +445,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         visit::walk_fn(self, fn_kind)
     }
 
-    fn visit_assoc_item_constraint(&mut self, constraint: &'a AssocItemConstraint) {
-        if let AssocItemConstraintKind::Bound { .. } = constraint.kind
-            && let Some(ast::GenericArgs::Parenthesized(args)) = constraint.gen_args.as_ref()
-            && args.inputs.is_empty()
-            && let ast::FnRetTy::Default(..) = args.output
-        {
-            gate!(
-                &self,
-                return_type_notation,
-                constraint.span,
-                "return type notation is experimental"
-            );
-        }
-
-        visit::walk_assoc_item_constraint(self, constraint)
-    }
-
     fn visit_assoc_item(&mut self, i: &'a ast::AssocItem, ctxt: AssocCtxt) {
         let is_fn = match &i.kind {
             ast::AssocItemKind::Fn(_) => true,
@@ -566,6 +549,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
         unsafe_extern_blocks,
         "`unsafe extern {}` blocks and `safe` keyword are experimental"
     );
+    gate_all!(return_type_notation, "return type notation is experimental");
 
     if !visitor.features.never_patterns {
         if let Some(spans) = spans.get(&sym::never_patterns) {
@@ -611,10 +595,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
 
     gate_all_legacy_dont_use!(box_patterns, "box pattern syntax is experimental");
     gate_all_legacy_dont_use!(trait_alias, "trait aliases are experimental");
-    // Despite being a new feature, `where T: Trait<Assoc(): Sized>`, which is RTN syntax now,
-    // used to be gated under associated_type_bounds, which are right above, so RTN needs to
-    // be too.
-    gate_all_legacy_dont_use!(return_type_notation, "return type notation is experimental");
     gate_all_legacy_dont_use!(decl_macro, "`macro` is experimental");
     gate_all_legacy_dont_use!(try_blocks, "`try` blocks are unstable");
     gate_all_legacy_dont_use!(auto_traits, "`auto` traits are unstable");
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 0225c95dca8..0568d368d8c 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1060,6 +1060,11 @@ impl<'a> PrintState<'a> for State<'a> {
                 self.word(")");
                 self.print_fn_ret_ty(&data.output);
             }
+            ast::GenericArgs::ParenthesizedElided(_) => {
+                self.word("(");
+                self.word("..");
+                self.word(")");
+            }
         }
     }
 }
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
index f2f6594e686..5b13858f839 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs
@@ -5,6 +5,7 @@ use ast::{ForLoopKind, MatchKind};
 use itertools::{Itertools, Position};
 use rustc_ast::ptr::P;
 use rustc_ast::token;
+use rustc_ast::util::classify;
 use rustc_ast::util::literal::escape_byte_str_symbol;
 use rustc_ast::util::parser::{self, AssocOp, Fixity};
 use rustc_ast::{self as ast, BlockCheckMode};
@@ -540,7 +541,7 @@ impl<'a> State<'a> {
                 self.ibox(0);
                 self.print_block_with_attrs(blk, attrs);
             }
-            ast::ExprKind::Gen(capture_clause, blk, kind) => {
+            ast::ExprKind::Gen(capture_clause, blk, kind, _decl_span) => {
                 self.word_nbsp(kind.modifier());
                 self.print_capture_clause(*capture_clause);
                 // cbox/ibox in analogy to the `ExprKind::Block` arm above
@@ -610,9 +611,12 @@ impl<'a> State<'a> {
                 }
                 if let Some(expr) = opt_expr {
                     self.space();
-                    self.print_expr_maybe_paren(
+                    self.print_expr_cond_paren(
                         expr,
-                        parser::PREC_JUMP,
+                        // Parenthesize if required by precedence, or in the
+                        // case of `break 'inner: loop { break 'inner 1 } + 1`
+                        expr.precedence().order() < parser::PREC_JUMP
+                            || (opt_label.is_none() && classify::leading_labeled_expr(expr)),
                         fixup.subsequent_subexpression(),
                     );
                 }
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index f26f8711dd4..8eb44458137 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -6,8 +6,8 @@ use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 
-impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
-    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
+impl<'infcx, 'tcx> crate::MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
+    pub fn dcx(&self) -> DiagCtxtHandle<'infcx> {
         self.infcx.dcx()
     }
 
@@ -18,7 +18,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         place: &str,
         borrow_place: &str,
         value_place: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         self.dcx().create_err(crate::session_diagnostics::MoveBorrow {
             place,
             span,
@@ -34,7 +34,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         desc: &str,
         borrow_span: Span,
         borrow_desc: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             span,
@@ -54,7 +54,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         old_loan_span: Span,
         old_opt_via: &str,
         old_load_end_span: Option<Span>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_code_err!(
             self.dcx(),
@@ -101,7 +101,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         desc: &str,
         old_loan_span: Span,
         old_load_end_span: Option<Span>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
@@ -134,7 +134,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         noun_old: &str,
         old_opt_via: &str,
         previous_end_span: Option<Span>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
@@ -166,7 +166,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         old_opt_via: &str,
         previous_end_span: Option<Span>,
         second_borrow_desc: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let mut err = struct_span_code_err!(
             self.dcx(),
             new_loan_span,
@@ -198,7 +198,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         kind_old: &str,
         msg_old: &str,
         old_load_end_span: Option<Span>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let via = |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {msg})") };
         let mut err = struct_span_code_err!(
             self.dcx(),
@@ -239,7 +239,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         span: Span,
         borrow_span: Span,
         desc: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             span,
@@ -256,12 +256,12 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         span: Span,
         desc: &str,
         is_arg: bool,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
         struct_span_code_err!(self.dcx(), span, E0384, "cannot assign {} {}", msg, desc)
     }
 
-    pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> Diag<'tcx> {
+    pub(crate) fn cannot_assign(&self, span: Span, desc: &str) -> Diag<'infcx> {
         struct_span_code_err!(self.dcx(), span, E0594, "cannot assign to {}", desc)
     }
 
@@ -269,7 +269,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         move_from_span: Span,
         move_from_desc: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             move_from_span,
@@ -287,7 +287,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         move_from_span: Span,
         ty: Ty<'_>,
         is_index: Option<bool>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let type_name = match (&ty.kind(), is_index) {
             (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
             (&ty::Slice(_), _) => "slice",
@@ -308,7 +308,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         move_from_span: Span,
         container_ty: Ty<'_>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             move_from_span,
@@ -325,7 +325,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         verb: &str,
         optional_adverb_for_moved: &str,
         moved_path: Option<String>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let moved_path = moved_path.map(|mp| format!(": `{mp}`")).unwrap_or_default();
 
         struct_span_code_err!(
@@ -344,7 +344,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         span: Span,
         path: &str,
         reason: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             span,
@@ -362,7 +362,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         immutable_place: &str,
         immutable_section: &str,
         action: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             mutate_span,
@@ -380,7 +380,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         span: Span,
         yield_span: Span,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
         struct_span_code_err!(
             self.dcx(),
@@ -391,7 +391,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         .with_span_label(yield_span, "possible yield occurs here")
     }
 
-    pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'tcx> {
+    pub(crate) fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             borrow_span,
@@ -400,7 +400,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         )
     }
 
-    pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'tcx> {
+    pub(crate) fn path_does_not_live_long_enough(&self, span: Span, path: &str) -> Diag<'infcx> {
         struct_span_code_err!(self.dcx(), span, E0597, "{} does not live long enough", path,)
     }
 
@@ -410,7 +410,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         return_kind: &str,
         reference_desc: &str,
         path_desc: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             span,
@@ -433,7 +433,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         borrowed_path: &str,
         capture_span: Span,
         scope: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             closure_span,
@@ -445,7 +445,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         .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) -> Diag<'tcx> {
+    pub(crate) fn thread_local_value_does_not_live_long_enough(&self, span: Span) -> Diag<'infcx> {
         struct_span_code_err!(
             self.dcx(),
             span,
@@ -454,7 +454,7 @@ impl<'tcx> crate::MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         )
     }
 
-    pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'tcx> {
+    pub(crate) fn temporary_value_borrowed_for_too_long(&self, span: Span) -> Diag<'infcx> {
         struct_span_code_err!(self.dcx(), span, E0716, "temporary value dropped while borrowed",)
     }
 }
diff --git a/compiler/rustc_borrowck/src/constraints/mod.rs b/compiler/rustc_borrowck/src/constraints/mod.rs
index b54e05b2b34..bb2fc3b67e9 100644
--- a/compiler/rustc_borrowck/src/constraints/mod.rs
+++ b/compiler/rustc_borrowck/src/constraints/mod.rs
@@ -1,4 +1,6 @@
+use crate::region_infer::{ConstraintSccs, RegionDefinition, RegionTracker};
 use crate::type_check::Locations;
+use crate::universal_regions::UniversalRegions;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::{RegionVid, TyCtxt, VarianceDiagInfo};
@@ -48,6 +50,110 @@ impl<'tcx> OutlivesConstraintSet<'tcx> {
     ) -> &IndexSlice<OutlivesConstraintIndex, OutlivesConstraint<'tcx>> {
         &self.outlives
     }
+
+    /// Computes cycles (SCCs) in the graph of regions. In particular,
+    /// find all regions R1, R2 such that R1: R2 and R2: R1 and group
+    /// them into an SCC, and find the relationships between SCCs.
+    pub(crate) fn compute_sccs(
+        &self,
+        static_region: RegionVid,
+        definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
+    ) -> ConstraintSccs {
+        let constraint_graph = self.graph(definitions.len());
+        let region_graph = &constraint_graph.region_graph(self, static_region);
+        ConstraintSccs::new_with_annotation(&region_graph, |r| {
+            RegionTracker::new(r, &definitions[r])
+        })
+    }
+
+    /// This method handles Universe errors by rewriting the constraint
+    /// graph. For each strongly connected component in the constraint
+    /// graph such that there is a series of constraints
+    ///    A: B: C: ... : X  where
+    /// A's universe is smaller than X's and A is a placeholder,
+    /// add a constraint that A: 'static. This is a safe upper bound
+    /// in the face of borrow checker/trait solver limitations that will
+    /// eventually go away.
+    ///
+    /// For a more precise definition, see the documentation for
+    /// [`RegionTracker::has_incompatible_universes()`].
+    ///
+    /// This edge case used to be handled during constraint propagation
+    /// by iterating over the strongly connected components in the constraint
+    /// graph while maintaining a set of bookkeeping mappings similar
+    /// to what is stored in `RegionTracker` and manually adding 'sttaic as
+    /// needed.
+    ///
+    /// It was rewritten as part of the Polonius project with the goal of moving
+    /// higher-kindedness concerns out of the path of the borrow checker,
+    /// for two reasons:
+    ///
+    /// 1. Implementing Polonius is difficult enough without also
+    ///     handling them.
+    /// 2. The long-term goal is to handle higher-kinded concerns
+    ///     in the trait solver, where they belong. This avoids
+    ///     logic duplication and allows future trait solvers
+    ///     to compute better bounds than for example our
+    ///     "must outlive 'static" here.
+    ///
+    /// This code is a stop-gap measure in preparation for the future trait solver.
+    ///
+    /// Every constraint added by this method is an
+    /// internal `IllegalUniverse` constraint.
+    #[instrument(skip(self, universal_regions, definitions))]
+    pub(crate) fn add_outlives_static(
+        &mut self,
+        universal_regions: &UniversalRegions<'tcx>,
+        definitions: &IndexVec<RegionVid, RegionDefinition<'tcx>>,
+    ) -> ConstraintSccs {
+        let fr_static = universal_regions.fr_static;
+        let sccs = self.compute_sccs(fr_static, definitions);
+
+        // Changed to `true` if we added any constraints to `self` and need to
+        // recompute SCCs.
+        let mut added_constraints = false;
+
+        for scc in sccs.all_sccs() {
+            // No point in adding 'static: 'static!
+            // This micro-optimisation makes somewhat sense
+            // because static outlives *everything*.
+            if scc == sccs.scc(fr_static) {
+                continue;
+            }
+
+            let annotation = sccs.annotation(scc);
+
+            // If this SCC participates in a universe violation,
+            // e.g. if it reaches a region with a universe smaller than
+            // the largest region reached, add a requirement that it must
+            // outlive `'static`.
+            if annotation.has_incompatible_universes() {
+                // Optimisation opportunity: this will add more constraints than
+                // needed for correctness, since an SCC upstream of another with
+                // a universe violation will "infect" its downstream SCCs to also
+                // outlive static.
+                added_constraints = true;
+                let scc_representative_outlives_static = OutlivesConstraint {
+                    sup: annotation.representative,
+                    sub: fr_static,
+                    category: ConstraintCategory::IllegalUniverse,
+                    locations: Locations::All(rustc_span::DUMMY_SP),
+                    span: rustc_span::DUMMY_SP,
+                    variance_info: VarianceDiagInfo::None,
+                    from_closure: false,
+                };
+                self.push(scc_representative_outlives_static);
+            }
+        }
+
+        if added_constraints {
+            // We changed the constraint set and so must recompute SCCs.
+            self.compute_sccs(fr_static, definitions)
+        } else {
+            // If we didn't add any back-edges; no more work needs doing
+            sccs
+        }
+    }
 }
 
 impl<'tcx> Index<OutlivesConstraintIndex> for OutlivesConstraintSet<'tcx> {
diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs
index f2b5ddcd782..00a30dc2240 100644
--- a/compiler/rustc_borrowck/src/dataflow.rs
+++ b/compiler/rustc_borrowck/src/dataflow.rs
@@ -15,24 +15,24 @@ use std::fmt;
 use crate::{places_conflict, BorrowSet, PlaceConflictBias, PlaceExt, RegionInferenceContext};
 
 /// The results of the dataflow analyses used by the borrow checker.
-pub struct BorrowckResults<'mir, 'tcx> {
-    pub(crate) borrows: Results<'tcx, Borrows<'mir, 'tcx>>,
-    pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
-    pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'mir, 'tcx>>,
+pub struct BorrowckResults<'a, 'mir, 'tcx> {
+    pub(crate) borrows: Results<'tcx, Borrows<'a, 'mir, 'tcx>>,
+    pub(crate) uninits: Results<'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>,
+    pub(crate) ever_inits: Results<'tcx, EverInitializedPlaces<'a, 'mir, 'tcx>>,
 }
 
 /// The transient state of the dataflow analyses used by the borrow checker.
 #[derive(Debug)]
-pub struct BorrowckFlowState<'mir, 'tcx> {
-    pub(crate) borrows: <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
-    pub(crate) uninits: <MaybeUninitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
-    pub(crate) ever_inits: <EverInitializedPlaces<'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
+pub struct BorrowckFlowState<'a, 'mir, 'tcx> {
+    pub(crate) borrows: <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
+    pub(crate) uninits: <MaybeUninitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
+    pub(crate) ever_inits: <EverInitializedPlaces<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Domain,
 }
 
-impl<'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'mir, 'tcx> {
+impl<'a, 'mir, 'tcx> ResultsVisitable<'tcx> for BorrowckResults<'a, 'mir, 'tcx> {
     // All three analyses are forward, but we have to use just one here.
-    type Direction = <Borrows<'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction;
-    type FlowState = BorrowckFlowState<'mir, 'tcx>;
+    type Direction = <Borrows<'a, 'mir, 'tcx> as AnalysisDomain<'tcx>>::Direction;
+    type FlowState = BorrowckFlowState<'a, 'mir, 'tcx>;
 
     fn new_flow_state(&self, body: &mir::Body<'tcx>) -> Self::FlowState {
         BorrowckFlowState {
@@ -106,11 +106,11 @@ rustc_index::newtype_index! {
 /// `BorrowIndex`, and maps each such index to a `BorrowData`
 /// describing the borrow. These indexes are used for representing the
 /// borrows in compact bitvectors.
-pub struct Borrows<'mir, 'tcx> {
+pub struct Borrows<'a, 'mir, 'tcx> {
     tcx: TyCtxt<'tcx>,
     body: &'mir Body<'tcx>,
 
-    borrow_set: &'mir BorrowSet<'tcx>,
+    borrow_set: &'a BorrowSet<'tcx>,
     borrows_out_of_scope_at_location: FxIndexMap<Location, Vec<BorrowIndex>>,
 }
 
@@ -389,12 +389,12 @@ impl<'tcx> PoloniusOutOfScopePrecomputer<'_, 'tcx> {
     }
 }
 
-impl<'mir, 'tcx> Borrows<'mir, 'tcx> {
+impl<'a, 'mir, 'tcx> Borrows<'a, 'mir, 'tcx> {
     pub fn new(
         tcx: TyCtxt<'tcx>,
         body: &'mir Body<'tcx>,
-        regioncx: &'mir RegionInferenceContext<'tcx>,
-        borrow_set: &'mir BorrowSet<'tcx>,
+        regioncx: &RegionInferenceContext<'tcx>,
+        borrow_set: &'a BorrowSet<'tcx>,
     ) -> Self {
         let mut borrows_out_of_scope_at_location =
             calculate_borrows_out_of_scope_at_location(body, regioncx, borrow_set);
@@ -494,7 +494,7 @@ impl<'mir, 'tcx> Borrows<'mir, 'tcx> {
     }
 }
 
-impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
+impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, '_, 'tcx> {
     type Domain = BitSet<BorrowIndex>;
 
     const NAME: &'static str = "borrows";
@@ -517,7 +517,7 @@ impl<'tcx> rustc_mir_dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
 ///   region stops containing the CFG points reachable from the issuing location.
 /// - we also kill loans of conflicting places when overwriting a shared path: e.g. borrows of
 ///   `a.b.c` when `a` is overwritten.
-impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
+impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, '_, 'tcx> {
     type Idx = BorrowIndex;
 
     fn domain_size(&self, _: &mir::Body<'tcx>) -> usize {
@@ -617,8 +617,8 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
     }
 }
 
-impl DebugWithContext<Borrows<'_, '_>> for BorrowIndex {
-    fn fmt_with(&self, ctxt: &Borrows<'_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl DebugWithContext<Borrows<'_, '_, '_>> for BorrowIndex {
+    fn fmt_with(&self, ctxt: &Borrows<'_, '_, '_>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "{:?}", ctxt.location(*self))
     }
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index d46febffba8..8bf3e670ff2 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -149,13 +149,13 @@ trait TypeOpInfo<'tcx> {
 
     fn base_universe(&self) -> ty::UniverseIndex;
 
-    fn nice_error(
+    fn nice_error<'infcx>(
         &self,
-        mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
+        mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<Diag<'tcx>>;
+    ) -> Option<Diag<'infcx>>;
 
     #[instrument(level = "debug", skip(self, mbcx))]
     fn report_error(
@@ -231,18 +231,25 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
         self.base_universe
     }
 
-    fn nice_error(
+    fn nice_error<'infcx>(
         &self,
-        mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
+        mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Option<Diag<'infcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
         type_op_prove_predicate_with_cause(&ocx, key, cause);
-        try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region)
+        let diag = try_extract_error_from_fulfill_cx(
+            &ocx,
+            mbcx.mir_def_id(),
+            placeholder_region,
+            error_region,
+        )?
+        .with_dcx(mbcx.dcx());
+        Some(diag)
     }
 }
 
@@ -268,13 +275,13 @@ where
         self.base_universe
     }
 
-    fn nice_error(
+    fn nice_error<'infcx>(
         &self,
-        mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
+        mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Option<Diag<'infcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
@@ -288,7 +295,14 @@ where
         let (param_env, value) = key.into_parts();
         let _ = ocx.normalize(&cause, param_env, value.value);
 
-        try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region)
+        let diag = try_extract_error_from_fulfill_cx(
+            &ocx,
+            mbcx.mir_def_id(),
+            placeholder_region,
+            error_region,
+        )?
+        .with_dcx(mbcx.dcx());
+        Some(diag)
     }
 }
 
@@ -308,18 +322,25 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
         self.base_universe
     }
 
-    fn nice_error(
+    fn nice_error<'infcx>(
         &self,
-        mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
+        mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Option<Diag<'infcx>> {
         let (infcx, key, _) =
             mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query);
         let ocx = ObligationCtxt::new(&infcx);
         type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?;
-        try_extract_error_from_fulfill_cx(&ocx, mbcx.mir_def_id(), placeholder_region, error_region)
+        let diag = try_extract_error_from_fulfill_cx(
+            &ocx,
+            mbcx.mir_def_id(),
+            placeholder_region,
+            error_region,
+        )?
+        .with_dcx(mbcx.dcx());
+        Some(diag)
     }
 }
 
@@ -334,13 +355,13 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
         self.base_universe.unwrap()
     }
 
-    fn nice_error(
+    fn nice_error<'infcx>(
         &self,
-        mbcx: &mut MirBorrowckCtxt<'_, '_, '_, 'tcx>,
+        mbcx: &mut MirBorrowckCtxt<'_, '_, 'infcx, 'tcx>,
         _cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<Diag<'tcx>> {
+    ) -> Option<Diag<'infcx>> {
         try_extract_error_from_region_constraints(
             mbcx.infcx,
             mbcx.mir_def_id(),
@@ -358,12 +379,12 @@ impl<'tcx> TypeOpInfo<'tcx> for crate::type_check::InstantiateOpaqueType<'tcx> {
 }
 
 #[instrument(skip(ocx), level = "debug")]
-fn try_extract_error_from_fulfill_cx<'tcx>(
-    ocx: &ObligationCtxt<'_, 'tcx>,
+fn try_extract_error_from_fulfill_cx<'a, 'tcx>(
+    ocx: &ObligationCtxt<'a, 'tcx>,
     generic_param_scope: LocalDefId,
     placeholder_region: ty::Region<'tcx>,
     error_region: Option<ty::Region<'tcx>>,
-) -> Option<Diag<'tcx>> {
+) -> Option<Diag<'a>> {
     // We generally shouldn't have errors here because the query was
     // already run, but there's no point using `span_delayed_bug`
     // when we're going to emit an error here anyway.
@@ -381,15 +402,15 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
 }
 
 #[instrument(level = "debug", skip(infcx, region_var_origin, universe_of_region))]
-fn try_extract_error_from_region_constraints<'tcx>(
-    infcx: &InferCtxt<'tcx>,
+fn try_extract_error_from_region_constraints<'a, 'tcx>(
+    infcx: &'a InferCtxt<'tcx>,
     generic_param_scope: LocalDefId,
     placeholder_region: ty::Region<'tcx>,
     error_region: Option<ty::Region<'tcx>>,
     region_constraints: &RegionConstraintData<'tcx>,
     mut region_var_origin: impl FnMut(RegionVid) -> RegionVariableOrigin,
     mut universe_of_region: impl FnMut(RegionVid) -> UniverseIndex,
-) -> Option<Diag<'tcx>> {
+) -> Option<Diag<'a>> {
     let placeholder_universe = match placeholder_region.kind() {
         ty::RePlaceholder(p) => p.universe,
         ty::ReVar(vid) => universe_of_region(vid),
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 1cc7fee718e..b1e302e5e27 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -34,11 +34,12 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span, Symbol};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
 use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt};
 use std::iter;
+use std::ops::ControlFlow;
 
 use crate::borrow_set::TwoPhaseActivation;
 use crate::borrowck_errors;
@@ -73,7 +74,7 @@ enum StorageDeadOrDrop<'tcx> {
     Destructor(Ty<'tcx>),
 }
 
-impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
     pub(crate) fn report_use_of_moved_or_uninitialized(
         &mut self,
         location: Location,
@@ -341,7 +342,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
     fn suggest_ref_or_clone(
         &self,
         mpi: MovePathIndex,
-        err: &mut Diag<'tcx>,
+        err: &mut Diag<'infcx>,
         in_pattern: &mut bool,
         move_spans: UseSpans<'tcx>,
     ) {
@@ -517,7 +518,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         desired_action: InitializationRequiringAction,
         span: Span,
         use_spans: UseSpans<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         // We need all statements in the body where the binding was assigned to later find all
         // the branching code paths where the binding *wasn't* assigned to.
         let inits = &self.move_data.init_path_map[mpi];
@@ -784,20 +785,20 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         /// binding declaration within every scope we inspect.
         struct Finder {
             hir_id: hir::HirId,
-            found: bool,
         }
         impl<'hir> Visitor<'hir> for Finder {
-            fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) {
+            type Result = ControlFlow<()>;
+            fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) -> Self::Result {
                 if pat.hir_id == self.hir_id {
-                    self.found = true;
+                    return ControlFlow::Break(());
                 }
-                hir::intravisit::walk_pat(self, pat);
+                hir::intravisit::walk_pat(self, pat)
             }
-            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
+            fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) -> Self::Result {
                 if ex.hir_id == self.hir_id {
-                    self.found = true;
+                    return ControlFlow::Break(());
                 }
-                hir::intravisit::walk_expr(self, ex);
+                hir::intravisit::walk_expr(self, ex)
             }
         }
         // The immediate HIR parent of the moved expression. We'll look for it to be a call.
@@ -822,9 +823,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                 _ => continue,
             };
             if let Some(&hir_id) = local_hir_id {
-                let mut finder = Finder { hir_id, found: false };
-                finder.visit_expr(e);
-                if finder.found {
+                if (Finder { hir_id }).visit_expr(e).is_break() {
                     // The current scope includes the declaration of the binding we're accessing, we
                     // can't look up any further for loops.
                     break;
@@ -839,9 +838,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                         hir::Node::Expr(hir::Expr {
                             kind: hir::ExprKind::If(cond, ..), ..
                         }) => {
-                            let mut finder = Finder { hir_id: expr.hir_id, found: false };
-                            finder.visit_expr(cond);
-                            if finder.found {
+                            if (Finder { hir_id: expr.hir_id }).visit_expr(cond).is_break() {
                                 // The expression where the move error happened is in a `while let`
                                 // condition Don't suggest clone as it will likely end in an
                                 // infinite loop.
@@ -1288,7 +1285,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             return false;
         }
         // Try to find predicates on *generic params* that would allow copying `ty`
-        let suggestion =
+        let mut suggestion =
             if let Some(symbol) = tcx.hir().maybe_get_struct_pattern_shorthand_field(expr) {
                 format!(": {symbol}.clone()")
             } else {
@@ -1296,6 +1293,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             };
         let mut sugg = Vec::with_capacity(2);
         let mut inner_expr = expr;
+        let mut is_raw_ptr = false;
+        let typeck_result = self.infcx.tcx.typeck(self.mir_def_id());
         // Remove uses of `&` and `*` when suggesting `.clone()`.
         while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) =
             &inner_expr.kind
@@ -1306,14 +1305,32 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                 return false;
             }
             inner_expr = inner;
+            if let Some(inner_type) = typeck_result.node_type_opt(inner.hir_id) {
+                if matches!(inner_type.kind(), ty::RawPtr(..)) {
+                    is_raw_ptr = true;
+                    break;
+                }
+            }
         }
-        if inner_expr.span.lo() != expr.span.lo() {
+        // Cloning the raw pointer doesn't make sense in some cases and would cause a type mismatch error. (see #126863)
+        if inner_expr.span.lo() != expr.span.lo() && !is_raw_ptr {
+            // Remove "(*" or "(&"
             sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new()));
         }
+        // Check whether `expr` is surrounded by parentheses or not.
         let span = if inner_expr.span.hi() != expr.span.hi() {
             // Account for `(*x)` to suggest `x.clone()`.
-            expr.span.with_lo(inner_expr.span.hi())
+            if is_raw_ptr {
+                expr.span.shrink_to_hi()
+            } else {
+                // Remove the close parenthesis ")"
+                expr.span.with_lo(inner_expr.span.hi())
+            }
         } else {
+            if is_raw_ptr {
+                sugg.push((expr.span.shrink_to_lo(), "(".to_string()));
+                suggestion = ").clone()".to_string();
+            }
             expr.span.shrink_to_hi()
         };
         sugg.push((span, suggestion));
@@ -1441,7 +1458,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         location: Location,
         (place, _span): (Place<'tcx>, Span),
         borrow: &BorrowData<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
 
@@ -1491,7 +1508,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         (place, span): (Place<'tcx>, Span),
         gen_borrow_kind: BorrowKind,
         issued_borrow: &BorrowData<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
         let issued_span = issued_spans.args_or_use();
 
@@ -1782,7 +1799,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         err
     }
 
-    fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'tcx>, place: Place<'tcx>) {
+    fn suggest_copy_for_type_in_cloned_ref(&self, err: &mut Diag<'infcx>, place: Place<'tcx>) {
         let tcx = self.infcx.tcx;
         let hir = tcx.hir();
         let Some(body_id) = tcx.hir_node(self.mir_hir_id()).body_id() else { return };
@@ -1817,7 +1834,6 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
 
         pub struct Holds<'tcx> {
             ty: Ty<'tcx>,
-            holds: bool,
         }
 
         impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Holds<'tcx> {
@@ -1825,7 +1841,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
 
             fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
                 if t == self.ty {
-                    self.holds = true;
+                    return ControlFlow::Break(());
                 }
                 t.super_visit_with(self)
             }
@@ -1843,9 +1859,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                 && rcvr_ty == ty
                 && let ty::Ref(_, inner, _) = rcvr_ty.kind()
                 && let inner = inner.peel_refs()
-                && let mut v = (Holds { ty: inner, holds: false })
-                && let _ = v.visit_ty(local_ty)
-                && v.holds
+                && (Holds { ty: inner }).visit_ty(local_ty).is_break()
                 && let None = self.infcx.type_implements_trait_shallow(clone, inner, self.param_env)
             {
                 err.span_label(
@@ -2841,7 +2855,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         drop_span: Span,
         borrow_spans: UseSpans<'tcx>,
         explanation: BorrowExplanation<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         debug!(
             "report_local_value_does_not_live_long_enough(\
              {:?}, {:?}, {:?}, {:?}, {:?}\
@@ -3016,7 +3030,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         drop_span: Span,
         borrow_span: Span,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         debug!(
             "report_thread_local_value_does_not_live_long_enough(\
              {:?}, {:?}\
@@ -3041,7 +3055,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         borrow_spans: UseSpans<'tcx>,
         proper_span: Span,
         explanation: BorrowExplanation<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
             explanation
         {
@@ -3206,7 +3220,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         return_span: Span,
         category: ConstraintCategory<'tcx>,
         opt_place_desc: Option<&String>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'infcx>> {
         let return_kind = match category {
             ConstraintCategory::Return(_) => "return",
             ConstraintCategory::Yield => "yield",
@@ -3299,7 +3313,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         constraint_span: Span,
         captured_var: &str,
         scope: &str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let tcx = self.infcx.tcx;
         let args_span = use_span.args_or_use();
 
@@ -3411,7 +3425,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         upvar_span: Span,
         upvar_name: Symbol,
         escape_span: Span,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let tcx = self.infcx.tcx;
 
         let escapes_from = tcx.def_descr(self.mir_def_id().to_def_id());
@@ -3713,7 +3727,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             if tcx.is_diagnostic_item(sym::deref_method, method_did) {
                 let deref_target =
                     tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
-                        Instance::resolve(tcx, self.param_env, deref_target, method_args)
+                        Instance::try_resolve(tcx, self.param_env, deref_target, method_args)
                             .transpose()
                     });
                 if let Some(Ok(instance)) = deref_target {
@@ -3737,13 +3751,11 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         assigned_span: Span,
         err_place: Place<'tcx>,
     ) {
-        let (from_arg, local_decl, local_name) = match err_place.as_local() {
-            Some(local) => (
-                self.body.local_kind(local) == LocalKind::Arg,
-                Some(&self.body.local_decls[local]),
-                self.local_names[local],
-            ),
-            None => (false, None, None),
+        let (from_arg, local_decl) = match err_place.as_local() {
+            Some(local) => {
+                (self.body.local_kind(local) == LocalKind::Arg, Some(&self.body.local_decls[local]))
+            }
+            None => (false, None),
         };
 
         // If root local is initialized immediately (everything apart from let
@@ -3775,13 +3787,12 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             err.span_label(assigned_span, format!("first assignment to {place_description}"));
         }
         if let Some(decl) = local_decl
-            && let Some(name) = local_name
             && decl.can_be_made_mutable()
         {
-            err.span_suggestion(
-                decl.source_info.span,
+            err.span_suggestion_verbose(
+                decl.source_info.span.shrink_to_lo(),
                 "consider making this binding mutable",
-                format!("mut {name}"),
+                "mut ".to_string(),
                 Applicability::MachineApplicable,
             );
             if !from_arg
@@ -3793,10 +3804,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                     }))
                 )
             {
-                err.span_suggestion(
-                    decl.source_info.span,
+                err.span_suggestion_verbose(
+                    decl.source_info.span.shrink_to_lo(),
                     "to modify the original value, take a borrow instead",
-                    format!("ref mut {name}"),
+                    "ref mut ".to_string(),
                     Applicability::MaybeIncorrect,
                 );
             }
@@ -4308,15 +4319,14 @@ impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
 }
 
 /// Detect whether one of the provided spans is a statement nested within the top-most visited expr
-struct ReferencedStatementsVisitor<'a>(&'a [Span], bool);
+struct ReferencedStatementsVisitor<'a>(&'a [Span]);
 
-impl<'a, 'v> Visitor<'v> for ReferencedStatementsVisitor<'a> {
-    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) {
+impl<'v> Visitor<'v> for ReferencedStatementsVisitor<'_> {
+    type Result = ControlFlow<()>;
+    fn visit_stmt(&mut self, s: &'v hir::Stmt<'v>) -> Self::Result {
         match s.kind {
-            hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => {
-                self.1 = true;
-            }
-            _ => {}
+            hir::StmtKind::Semi(expr) if self.0.contains(&expr.span) => ControlFlow::Break(()),
+            _ => ControlFlow::Continue(()),
         }
     }
 }
@@ -4358,9 +4368,7 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> {
             hir::ExprKind::If(cond, body, None) => {
                 // `if` expressions with no `else` that initialize the binding might be missing an
                 // `else` arm.
-                let mut v = ReferencedStatementsVisitor(self.spans, false);
-                v.visit_expr(body);
-                if v.1 {
+                if ReferencedStatementsVisitor(self.spans).visit_expr(body).is_break() {
                     self.errors.push((
                         cond.span,
                         format!(
@@ -4377,11 +4385,9 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> {
             hir::ExprKind::If(cond, body, Some(other)) => {
                 // `if` expressions where the binding is only initialized in one of the two arms
                 // might be missing a binding initialization.
-                let mut a = ReferencedStatementsVisitor(self.spans, false);
-                a.visit_expr(body);
-                let mut b = ReferencedStatementsVisitor(self.spans, false);
-                b.visit_expr(other);
-                match (a.1, b.1) {
+                let a = ReferencedStatementsVisitor(self.spans).visit_expr(body).is_break();
+                let b = ReferencedStatementsVisitor(self.spans).visit_expr(other).is_break();
+                match (a, b) {
                     (true, true) | (false, false) => {}
                     (true, false) => {
                         if other.span.is_desugaring(DesugaringKind::WhileLoop) {
@@ -4420,11 +4426,7 @@ impl<'b, 'v, 'tcx> Visitor<'v> for ConditionVisitor<'b, 'tcx> {
                 // arms might be missing an initialization.
                 let results: Vec<bool> = arms
                     .iter()
-                    .map(|arm| {
-                        let mut v = ReferencedStatementsVisitor(self.spans, false);
-                        v.visit_arm(arm);
-                        v.1
-                    })
+                    .map(|arm| ReferencedStatementsVisitor(self.spans).visit_arm(arm).is_break())
                     .collect();
                 if results.iter().any(|x| *x) && !results.iter().all(|x| *x) {
                     for (arm, seen) in arms.iter().zip(results) {
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 6165a718a30..ffe52f939dd 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, RegionVid, Ty, TyCtxt};
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{sym, DesugaringKind, Span};
-use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
+use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 
 use crate::region_infer::{BlameConstraint, ExtraConstraintInfo};
 use crate::{
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 842ed38f1e2..b7fbb71a0cf 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -27,8 +27,8 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::source_map::Spanned;
 use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, VariantIdx};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{
     type_known_to_meet_bound_modulo_regions, FulfillmentErrorCode,
 };
@@ -69,7 +69,7 @@ pub(super) struct DescribePlaceOpt {
 
 pub(super) struct IncludingTupleField(pub(super) bool);
 
-impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
     /// Adds a suggestion when a closure is invoked twice with a moved variable or when a closure
     /// is moved after being invoked.
     ///
@@ -86,7 +86,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         location: Location,
         place: PlaceRef<'tcx>,
-        diag: &mut Diag<'_>,
+        diag: &mut Diag<'infcx>,
     ) -> bool {
         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
         let mut target = place.local_or_deref_local();
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 12fa4c4f5ee..4b6c1b29f28 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -9,7 +9,7 @@ use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
 use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
-use rustc_trait_selection::traits::error_reporting::FindExprBySpan;
+use rustc_trait_selection::error_reporting::traits::FindExprBySpan;
 
 use crate::diagnostics::CapturedMessageOpt;
 use crate::diagnostics::{DescribePlaceOpt, UseSpans};
@@ -93,7 +93,7 @@ enum GroupedMoveError<'tcx> {
     },
 }
 
-impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
     pub(crate) fn report_move_errors(&mut self) {
         let grouped_errors = self.group_move_errors();
         for error in grouped_errors {
@@ -291,7 +291,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         self.buffer_error(err);
     }
 
-    fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'tcx> {
+    fn report_cannot_move_from_static(&mut self, place: Place<'tcx>, span: Span) -> Diag<'infcx> {
         let description = if place.projection.len() == 1 {
             format!("static item {}", self.describe_any_place(place.as_ref()))
         } else {
@@ -428,7 +428,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         deref_target_place: Place<'tcx>,
         span: Span,
         use_spans: Option<UseSpans<'tcx>>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let tcx = self.infcx.tcx;
         // Inspect the type of the content behind the
         // borrow to provide feedback about why this
@@ -639,12 +639,27 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
     fn add_borrow_suggestions(&self, err: &mut Diag<'_>, span: Span) {
         match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
             Ok(snippet) if snippet.starts_with('*') => {
-                err.span_suggestion_verbose(
-                    span.with_hi(span.lo() + BytePos(1)),
-                    "consider removing the dereference here",
-                    String::new(),
-                    Applicability::MaybeIncorrect,
-                );
+                let sp = span.with_lo(span.lo() + BytePos(1));
+                let inner = self.find_expr(sp);
+                let mut is_raw_ptr = false;
+                if let Some(inner) = inner {
+                    let typck_result = self.infcx.tcx.typeck(self.mir_def_id());
+                    if let Some(inner_type) = typck_result.node_type_opt(inner.hir_id) {
+                        if matches!(inner_type.kind(), ty::RawPtr(..)) {
+                            is_raw_ptr = true;
+                        }
+                    }
+                }
+                // If the `inner` is a raw pointer, do not suggest removing the "*", see #126863
+                // FIXME: need to check whether the assigned object can be a raw pointer, see `tests/ui/borrowck/issue-20801.rs`.
+                if !is_raw_ptr {
+                    err.span_suggestion_verbose(
+                        span.with_hi(span.lo() + BytePos(1)),
+                        "consider removing the dereference here",
+                        String::new(),
+                        Applicability::MaybeIncorrect,
+                    );
+                }
             }
             _ => {
                 err.span_suggestion_verbose(
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 93fac3181ba..26b0d23b166 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -16,9 +16,9 @@ use rustc_middle::{
 use rustc_span::symbol::{kw, Symbol};
 use rustc_span::{sym, BytePos, DesugaringKind, Span};
 use rustc_target::abi::FieldIdx;
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
 
 use crate::diagnostics::BorrowedContentSource;
 use crate::util::FindAssignments;
@@ -30,7 +30,7 @@ pub(crate) enum AccessKind {
     Mutate,
 }
 
-impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
     pub(crate) fn report_mutability_error(
         &mut self,
         access_place: Place<'tcx>,
@@ -408,10 +408,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                                     fn_decl.implicit_self,
                                     hir::ImplicitSelfKind::RefImm | hir::ImplicitSelfKind::RefMut
                                 ) {
-                                    err.span_suggestion(
-                                        upvar_ident.span,
+                                    err.span_suggestion_verbose(
+                                        upvar_ident.span.shrink_to_lo(),
                                         "consider changing this to be mutable",
-                                        format!("mut {}", upvar_ident.name),
+                                        "mut ",
                                         Applicability::MachineApplicable,
                                     );
                                     break;
@@ -419,10 +419,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                             }
                         }
                     } else {
-                        err.span_suggestion(
-                            upvar_ident.span,
+                        err.span_suggestion_verbose(
+                            upvar_ident.span.shrink_to_lo(),
                             "consider changing this to be mutable",
-                            format!("mut {}", upvar_ident.name),
+                            "mut ",
                             Applicability::MachineApplicable,
                         );
                     }
@@ -449,8 +449,8 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                     .is_ok_and(|snippet| snippet.starts_with("&mut ")) =>
             {
                 err.span_label(span, format!("cannot {act}"));
-                err.span_suggestion(
-                    span,
+                err.span_suggestion_verbose(
+                    span.with_hi(span.lo() + BytePos(5)),
                     "try removing `&mut` here",
                     "",
                     Applicability::MaybeIncorrect,
@@ -541,7 +541,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
     }
 
     /// Suggest `map[k] = v` => `map.insert(k, v)` and the like.
-    fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'tcx>, span: Span) {
+    fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diag<'infcx>, span: Span) {
         let Some(adt) = ty.ty_adt_def() else { return };
         let did = adt.did();
         if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
@@ -550,13 +550,13 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
             /// Walks through the HIR, looking for the corresponding span for this error.
             /// When it finds it, see if it corresponds to assignment operator whose LHS
             /// is an index expr.
-            struct SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
+            struct SuggestIndexOperatorAlternativeVisitor<'a, 'infcx, 'tcx> {
                 assign_span: Span,
-                err: &'a mut Diag<'tcx>,
+                err: &'a mut Diag<'infcx>,
                 ty: Ty<'tcx>,
                 suggested: bool,
             }
-            impl<'a, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'tcx> {
+            impl<'a, 'cx, 'tcx> Visitor<'tcx> for SuggestIndexOperatorAlternativeVisitor<'a, 'cx, 'tcx> {
                 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
                     hir::intravisit::walk_stmt(self, stmt);
                     let expr = match stmt.kind {
@@ -755,13 +755,16 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                 pat: hir::Pat { kind: hir::PatKind::Ref(_, _), .. },
                 ..
             }) = node
-            && let Ok(name) =
-                self.infcx.tcx.sess.source_map().span_to_snippet(local_decl.source_info.span)
         {
-            err.span_suggestion(
-                pat_span,
+            err.multipart_suggestion(
                 "consider changing this to be mutable",
-                format!("&(mut {name})"),
+                vec![
+                    (pat_span.until(local_decl.source_info.span), "&(mut ".to_string()),
+                    (
+                        local_decl.source_info.span.shrink_to_hi().with_hi(pat_span.hi()),
+                        ")".to_string(),
+                    ),
+                ],
                 Applicability::MachineApplicable,
             );
             return;
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index db78edc45b9..55147ee337f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -10,14 +10,12 @@ use rustc_hir::GenericBound::Trait;
 use rustc_hir::QPath::Resolved;
 use rustc_hir::WherePredicate::BoundPredicate;
 use rustc_hir::{PolyTraitRef, TyKind, WhereBoundPredicate};
-use rustc_infer::infer::{
-    error_reporting::nice_region_error::{
-        self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
-        HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor,
-    },
-    error_reporting::unexpected_hidden_region_diagnostic,
-    NllRegionVariableOrigin, RelateParamBound,
+use rustc_infer::infer::error_reporting::nice_region_error::{
+    self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
+    HirTraitObjectVisitor, NiceRegionError, TraitObjectVisitor,
 };
+use rustc_infer::infer::error_reporting::region::unexpected_hidden_region_diagnostic;
+use rustc_infer::infer::{NllRegionVariableOrigin, RelateParamBound};
 use rustc_middle::bug;
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
@@ -66,7 +64,8 @@ impl<'tcx> ConstraintDescription for ConstraintCategory<'tcx> {
             ConstraintCategory::Predicate(_)
             | ConstraintCategory::Boring
             | ConstraintCategory::BoringNoLocation
-            | ConstraintCategory::Internal => "",
+            | ConstraintCategory::Internal
+            | ConstraintCategory::IllegalUniverse => "",
         }
     }
 }
@@ -160,7 +159,7 @@ pub struct ErrorConstraintInfo<'tcx> {
     pub(super) span: Span,
 }
 
-impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
+impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
     /// Converts a region inference variable into a `ty::Region` that
     /// we can use for error reporting. If `r` is universally bound,
     /// then we use the name that we have on record for it. If `r` is
@@ -360,7 +359,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                     let named_key = self.regioncx.name_regions(self.infcx.tcx, key);
                     let named_region = self.regioncx.name_regions(self.infcx.tcx, member_region);
                     let diag = unexpected_hidden_region_diagnostic(
-                        self.infcx.tcx,
+                        self.infcx,
                         self.mir_def_id(),
                         span,
                         named_ty,
@@ -589,7 +588,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
         &self,
         errci: &ErrorConstraintInfo<'tcx>,
         kind: ReturnConstraint,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'infcx> {
         let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
 
         let mut output_ty = self.regioncx.universal_regions().unnormalized_output_ty;
@@ -658,7 +657,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
     ///    |     ^^^^^^^^^^ `x` escapes the function body here
     /// ```
     #[instrument(level = "debug", skip(self))]
-    fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> {
+    fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> {
         let ErrorConstraintInfo { span, category, .. } = errci;
 
         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
@@ -767,7 +766,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
     ///    |                    is returning data with lifetime `'b`
     /// ```
     #[allow(rustc::diagnostic_outside_of_impl)] // FIXME
-    fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'tcx> {
+    fn report_general_error(&self, errci: &ErrorConstraintInfo<'tcx>) -> Diag<'infcx> {
         let ErrorConstraintInfo {
             fr,
             fr_is_local,
@@ -948,7 +947,7 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
                 return;
             }
 
-            if let Ok(Some(instance)) = ty::Instance::resolve(
+            if let Ok(Some(instance)) = ty::Instance::try_resolve(
                 tcx,
                 self.param_env,
                 *fn_did,
diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs
index 51ea59e2092..af96f115385 100644
--- a/compiler/rustc_borrowck/src/facts.rs
+++ b/compiler/rustc_borrowck/src/facts.rs
@@ -213,8 +213,32 @@ trait FactCell {
     fn to_string(&self, location_table: &LocationTable) -> String;
 }
 
-impl<A: Debug> FactCell for A {
-    default fn to_string(&self, _location_table: &LocationTable) -> String {
+impl FactCell for BorrowIndex {
+    fn to_string(&self, _location_table: &LocationTable) -> String {
+        format!("{self:?}")
+    }
+}
+
+impl FactCell for Local {
+    fn to_string(&self, _location_table: &LocationTable) -> String {
+        format!("{self:?}")
+    }
+}
+
+impl FactCell for MovePathIndex {
+    fn to_string(&self, _location_table: &LocationTable) -> String {
+        format!("{self:?}")
+    }
+}
+
+impl FactCell for PoloniusRegionVid {
+    fn to_string(&self, _location_table: &LocationTable) -> String {
+        format!("{self:?}")
+    }
+}
+
+impl FactCell for RegionVid {
+    fn to_string(&self, _location_table: &LocationTable) -> String {
         format!("{self:?}")
     }
 }
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 69efee2fbdc..d258c68b959 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -7,7 +7,6 @@
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
 #![feature(let_chains)]
-#![feature(min_specialization)]
 #![feature(never_type)]
 #![feature(rustc_attrs)]
 #![feature(rustdoc_internals)]
@@ -310,8 +309,8 @@ fn do_mir_borrowck<'tcx>(
         promoted_mbcx.report_move_errors();
         diags = promoted_mbcx.diags;
 
-        struct MoveVisitor<'a, 'b, 'mir, 'cx, 'tcx> {
-            ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'cx, 'tcx>,
+        struct MoveVisitor<'a, 'b, 'mir, 'infcx, 'tcx> {
+            ctxt: &'a mut MirBorrowckCtxt<'b, 'mir, 'infcx, 'tcx>,
         }
 
         impl<'tcx> Visitor<'tcx> for MoveVisitor<'_, '_, '_, '_, 'tcx> {
@@ -528,8 +527,8 @@ impl<'tcx> Deref for BorrowckInferCtxt<'tcx> {
     }
 }
 
-struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> {
-    infcx: &'cx BorrowckInferCtxt<'tcx>,
+struct MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx> {
+    infcx: &'infcx BorrowckInferCtxt<'tcx>,
     param_env: ParamEnv<'tcx>,
     body: &'mir Body<'tcx>,
     move_data: &'a MoveData<'tcx>,
@@ -596,7 +595,7 @@ struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> {
     /// Results of Polonius analysis.
     polonius_output: Option<Rc<PoloniusOutput>>,
 
-    diags: diags::BorrowckDiags<'tcx>,
+    diags: diags::BorrowckDiags<'infcx, 'tcx>,
     move_errors: Vec<MoveError<'tcx>>,
 }
 
@@ -605,15 +604,15 @@ struct MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx> {
 // 2. loans made in overlapping scopes do not conflict
 // 3. assignments do not affect things loaned out as immutable
 // 4. moves do not affect things loaned out in any way
-impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
-    for MirBorrowckCtxt<'_, 'mir, '_, 'tcx>
+impl<'a, 'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
+    for MirBorrowckCtxt<'a, 'mir, '_, 'tcx>
 {
-    type FlowState = Flows<'mir, 'tcx>;
+    type FlowState = Flows<'a, 'mir, 'tcx>;
 
     fn visit_statement_before_primary_effect(
         &mut self,
         _results: &mut R,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
         stmt: &'mir Statement<'tcx>,
         location: Location,
     ) {
@@ -683,7 +682,7 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
     fn visit_terminator_before_primary_effect(
         &mut self,
         _results: &mut R,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
         term: &'mir Terminator<'tcx>,
         loc: Location,
     ) {
@@ -728,6 +727,12 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
                 }
                 self.mutate_place(loc, (*destination, span), Deep, flow_state);
             }
+            TerminatorKind::TailCall { func, args, fn_span: _ } => {
+                self.consume_operand(loc, (func, span), flow_state);
+                for arg in args {
+                    self.consume_operand(loc, (&arg.node, arg.span), flow_state);
+                }
+            }
             TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
                 self.consume_operand(loc, (cond, span), flow_state);
                 if let AssertKind::BoundsCheck { len, index } = &**msg {
@@ -794,7 +799,7 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
     fn visit_terminator_after_primary_effect(
         &mut self,
         _results: &mut R,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
         term: &'mir Terminator<'tcx>,
         loc: Location,
     ) {
@@ -814,9 +819,8 @@ impl<'mir, 'tcx, R> rustc_mir_dataflow::ResultsVisitor<'mir, 'tcx, R>
 
             TerminatorKind::UnwindResume
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::CoroutineDrop => {
-                // Returning from the function implicitly kills storage for all locals and statics.
-                // Often, the storage will already have been killed by an explicit
                 // StorageDead, but we don't always emit those (notably on unwind paths),
                 // so this "extra check" serves as a kind of backup.
                 let borrow_set = self.borrow_set.clone();
@@ -988,7 +992,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         place_span: (Place<'tcx>, Span),
         kind: (AccessDepth, ReadOrWrite),
         is_local_mutation_allowed: LocalMutationIsAllowed,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         let (sd, rw) = kind;
 
@@ -1038,7 +1042,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         place_span: (Place<'tcx>, Span),
         sd: AccessDepth,
         rw: ReadOrWrite,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) -> bool {
         let mut error_reported = false;
         let borrow_set = Rc::clone(&self.borrow_set);
@@ -1179,7 +1183,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         location: Location,
         place_span: (Place<'tcx>, Span),
         kind: AccessDepth,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         // Write of P[i] or *P requires P init'd.
         self.check_if_assigned_path_is_moved(location, place_span, flow_state);
@@ -1197,7 +1201,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         &mut self,
         location: Location,
         (rvalue, span): (&'mir Rvalue<'tcx>, Span),
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         match rvalue {
             &Rvalue::Ref(_ /*rgn*/, bk, place) => {
@@ -1455,7 +1459,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         &mut self,
         location: Location,
         (operand, span): (&'mir Operand<'tcx>, Span),
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         match *operand {
             Operand::Copy(place) => {
@@ -1579,7 +1583,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         &mut self,
         location: Location,
         span: Span,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         // Two-phase borrow support: For each activation that is newly
         // generated at this statement, check if it interferes with
@@ -1743,7 +1747,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         location: Location,
         desired_action: InitializationRequiringAction,
         place_span: (PlaceRef<'tcx>, Span),
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
 
@@ -1848,7 +1852,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         location: Location,
         desired_action: InitializationRequiringAction,
         place_span: (PlaceRef<'tcx>, Span),
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         let maybe_uninits = &flow_state.uninits;
 
@@ -1947,7 +1951,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         &mut self,
         location: Location,
         (place, span): (Place<'tcx>, Span),
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) {
         debug!("check_if_assigned_path_is_moved place: {:?}", place);
 
@@ -2013,7 +2017,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
             location: Location,
             base: PlaceRef<'tcx>,
             span: Span,
-            flow_state: &Flows<'mir, 'tcx>,
+            flow_state: &Flows<'_, 'mir, 'tcx>,
         ) {
             // rust-lang/rust#21232: Until Rust allows reads from the
             // initialized parts of partially initialized structs, we
@@ -2104,7 +2108,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
         (place, span): (Place<'tcx>, Span),
         kind: ReadOrWrite,
         is_local_mutation_allowed: LocalMutationIsAllowed,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
         location: Location,
     ) -> bool {
         debug!(
@@ -2220,7 +2224,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
     fn is_local_ever_initialized(
         &self,
         local: Local,
-        flow_state: &Flows<'mir, 'tcx>,
+        flow_state: &Flows<'_, 'mir, 'tcx>,
     ) -> Option<InitIndex> {
         let mpi = self.move_data.rev_lookup.find_local(local)?;
         let ii = &self.move_data.init_path_map[mpi];
@@ -2228,7 +2232,7 @@ impl<'mir, 'tcx> MirBorrowckCtxt<'_, 'mir, '_, 'tcx> {
     }
 
     /// Adds the place into the used mutable variables set
-    fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'mir, 'tcx>) {
+    fn add_used_mut(&mut self, root_place: RootPlace<'tcx>, flow_state: &Flows<'_, 'mir, 'tcx>) {
         match root_place {
             RootPlace { place_local: local, place_projection: [], is_local_mutation_allowed } => {
                 // If the local may have been initialized, and it is now currently being
@@ -2428,12 +2432,12 @@ mod diags {
 
     use super::*;
 
-    enum BufferedDiag<'tcx> {
-        Error(Diag<'tcx>),
-        NonError(Diag<'tcx, ()>),
+    enum BufferedDiag<'infcx> {
+        Error(Diag<'infcx>),
+        NonError(Diag<'infcx, ()>),
     }
 
-    impl<'tcx> BufferedDiag<'tcx> {
+    impl<'infcx> BufferedDiag<'infcx> {
         fn sort_span(&self) -> Span {
             match self {
                 BufferedDiag::Error(diag) => diag.sort_span,
@@ -2442,7 +2446,7 @@ mod diags {
         }
     }
 
-    pub struct BorrowckDiags<'tcx> {
+    pub struct BorrowckDiags<'infcx, 'tcx> {
         /// This field keeps track of move errors that are to be reported for given move indices.
         ///
         /// There are situations where many errors can be reported for a single move out (see
@@ -2457,15 +2461,15 @@ mod diags {
         /// `BTreeMap` is used to preserve the order of insertions when iterating. This is necessary
         /// when errors in the map are being re-added to the error buffer so that errors with the
         /// same primary span come out in a consistent order.
-        buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'tcx>)>,
+        buffered_move_errors: BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, Diag<'infcx>)>,
 
-        buffered_mut_errors: FxIndexMap<Span, (Diag<'tcx>, usize)>,
+        buffered_mut_errors: FxIndexMap<Span, (Diag<'infcx>, usize)>,
 
         /// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics.
-        buffered_diags: Vec<BufferedDiag<'tcx>>,
+        buffered_diags: Vec<BufferedDiag<'infcx>>,
     }
 
-    impl<'tcx> BorrowckDiags<'tcx> {
+    impl<'infcx, 'tcx> BorrowckDiags<'infcx, 'tcx> {
         pub fn new() -> Self {
             BorrowckDiags {
                 buffered_move_errors: BTreeMap::new(),
@@ -2474,28 +2478,28 @@ mod diags {
             }
         }
 
-        pub fn buffer_error(&mut self, diag: Diag<'tcx>) {
+        pub fn buffer_error(&mut self, diag: Diag<'infcx>) {
             self.buffered_diags.push(BufferedDiag::Error(diag));
         }
 
-        pub fn buffer_non_error(&mut self, diag: Diag<'tcx, ()>) {
+        pub fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
             self.buffered_diags.push(BufferedDiag::NonError(diag));
         }
     }
 
-    impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
-        pub fn buffer_error(&mut self, diag: Diag<'tcx>) {
+    impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
+        pub fn buffer_error(&mut self, diag: Diag<'infcx>) {
             self.diags.buffer_error(diag);
         }
 
-        pub fn buffer_non_error(&mut self, diag: Diag<'tcx, ()>) {
+        pub fn buffer_non_error(&mut self, diag: Diag<'infcx, ()>) {
             self.diags.buffer_non_error(diag);
         }
 
         pub fn buffer_move_error(
             &mut self,
             move_out_indices: Vec<MoveOutIndex>,
-            place_and_err: (PlaceRef<'tcx>, Diag<'tcx>),
+            place_and_err: (PlaceRef<'tcx>, Diag<'infcx>),
         ) -> bool {
             if let Some((_, diag)) =
                 self.diags.buffered_move_errors.insert(move_out_indices, place_and_err)
@@ -2508,12 +2512,12 @@ mod diags {
             }
         }
 
-        pub fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'tcx>, usize)> {
+        pub fn get_buffered_mut_error(&mut self, span: Span) -> Option<(Diag<'infcx>, usize)> {
             // FIXME(#120456) - is `swap_remove` correct?
             self.diags.buffered_mut_errors.swap_remove(&span)
         }
 
-        pub fn buffer_mut_error(&mut self, span: Span, diag: Diag<'tcx>, count: usize) {
+        pub fn buffer_mut_error(&mut self, span: Span, diag: Diag<'infcx>, count: usize) {
             self.diags.buffered_mut_errors.insert(span, (diag, count));
         }
 
@@ -2554,7 +2558,7 @@ mod diags {
         pub fn has_move_error(
             &self,
             move_out_indices: &[MoveOutIndex],
-        ) -> Option<&(PlaceRef<'tcx>, Diag<'tcx>)> {
+        ) -> Option<&(PlaceRef<'tcx>, Diag<'infcx>)> {
             self.diags.buffered_move_errors.get(move_out_indices)
         }
     }
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index 923cf7e9405..2ffa9ba5b4d 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -81,7 +81,7 @@ pub(crate) fn compute_regions<'cx, 'tcx>(
     promoted: &IndexSlice<Promoted, Body<'tcx>>,
     location_table: &LocationTable,
     param_env: ty::ParamEnv<'tcx>,
-    flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'cx, 'tcx>>,
+    flow_inits: &mut ResultsCursor<'cx, 'tcx, MaybeInitializedPlaces<'_, 'cx, 'tcx>>,
     move_data: &MoveData<'tcx>,
     borrow_set: &BorrowSet<'tcx>,
     upvars: &[&ty::CapturedPlace<'tcx>],
@@ -262,13 +262,13 @@ pub(super) fn dump_mir_results<'tcx>(
 
 #[allow(rustc::diagnostic_outside_of_impl)]
 #[allow(rustc::untranslatable_diagnostic)]
-pub(super) fn dump_annotation<'tcx>(
-    infcx: &BorrowckInferCtxt<'tcx>,
+pub(super) fn dump_annotation<'tcx, 'cx>(
+    infcx: &'cx BorrowckInferCtxt<'tcx>,
     body: &Body<'tcx>,
     regioncx: &RegionInferenceContext<'tcx>,
     closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
     opaque_type_values: &FxIndexMap<LocalDefId, OpaqueHiddenType<'tcx>>,
-    diags: &mut crate::diags::BorrowckDiags<'tcx>,
+    diags: &mut crate::diags::BorrowckDiags<'cx, 'tcx>,
 ) {
     let tcx = infcx.tcx;
     let base_def_id = tcx.typeck_root_def_id(body.source.def_id());
@@ -285,7 +285,7 @@ pub(super) fn dump_annotation<'tcx>(
 
     let def_span = tcx.def_span(body.source.def_id());
     let mut err = if let Some(closure_region_requirements) = closure_region_requirements {
-        let mut err = tcx.dcx().struct_span_note(def_span, "external requirements");
+        let mut err = infcx.dcx().struct_span_note(def_span, "external requirements");
 
         regioncx.annotate(tcx, &mut err);
 
@@ -304,7 +304,7 @@ pub(super) fn dump_annotation<'tcx>(
 
         err
     } else {
-        let mut err = tcx.dcx().struct_span_note(def_span, "no external requirements");
+        let mut err = infcx.dcx().struct_span_note(def_span, "no external requirements");
         regioncx.annotate(tcx, &mut err);
 
         err
diff --git a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
index 6979910a02d..30dfc4c21b0 100644
--- a/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
+++ b/compiler/rustc_borrowck/src/polonius/loan_invalidations.rs
@@ -125,6 +125,12 @@ impl<'cx, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'cx, 'tcx> {
                 }
                 self.mutate_place(location, *destination, Deep);
             }
+            TerminatorKind::TailCall { func, args, .. } => {
+                self.consume_operand(location, func);
+                for arg in args {
+                    self.consume_operand(location, &arg.node);
+                }
+            }
             TerminatorKind::Assert { cond, expected: _, msg, target: _, unwind: _ } => {
                 self.consume_operand(location, cond);
                 use rustc_middle::mir::AssertKind;
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index c56eaaff076..44a84fb9d7f 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -62,7 +62,7 @@ pub struct RegionTracker {
     /// The representative Region Variable Id for this SCC. We prefer
     /// placeholders over existentially quantified variables, otherwise
     ///  it's the one with the smallest Region Variable ID.
-    representative: RegionVid,
+    pub(crate) representative: RegionVid,
 
     /// Is the current representative a placeholder?
     representative_is_placeholder: bool,
@@ -97,7 +97,7 @@ impl scc::Annotation for RegionTracker {
 }
 
 impl RegionTracker {
-    fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
+    pub(crate) fn new(rvid: RegionVid, definition: &RegionDefinition<'_>) -> Self {
         let (representative_is_placeholder, representative_is_existential) = match definition.origin
         {
             rustc_infer::infer::NllRegionVariableOrigin::FreeRegion => (false, false),
@@ -116,7 +116,9 @@ impl RegionTracker {
             representative_is_existential,
         }
     }
-    fn universe(self) -> UniverseIndex {
+
+    /// The smallest-indexed universe reachable from and/or in this SCC.
+    fn min_universe(self) -> UniverseIndex {
         self.min_reachable_universe
     }
 
@@ -132,8 +134,8 @@ impl RegionTracker {
 
     /// Returns `true` if during the annotated SCC reaches a placeholder
     /// with a universe larger than the smallest reachable one, `false` otherwise.
-    pub fn has_incompatible_universes(&self) -> bool {
-        self.universe().cannot_name(self.max_placeholder_universe_reached)
+    pub(crate) fn has_incompatible_universes(&self) -> bool {
+        self.min_universe().cannot_name(self.max_placeholder_universe_reached)
     }
 }
 
@@ -163,7 +165,7 @@ pub struct RegionInferenceContext<'tcx> {
     /// The SCC computed from `constraints` and the constraint
     /// graph. We have an edge from SCC A to SCC B if `A: B`. Used to
     /// compute the values of each region.
-    constraint_sccs: Rc<ConstraintSccs>,
+    constraint_sccs: ConstraintSccs,
 
     /// Reverse of the SCC constraint graph --  i.e., an edge `A -> B` exists if
     /// `B: A`. This is used to compute the universal regions that are required
@@ -401,7 +403,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         universal_regions: Rc<UniversalRegions<'tcx>>,
         placeholder_indices: Rc<PlaceholderIndices>,
         universal_region_relations: Frozen<UniversalRegionRelations<'tcx>>,
-        outlives_constraints: OutlivesConstraintSet<'tcx>,
+        mut outlives_constraints: OutlivesConstraintSet<'tcx>,
         member_constraints_in: MemberConstraintSet<'tcx, RegionVid>,
         universe_causes: FxIndexMap<ty::UniverseIndex, UniverseInfo<'tcx>>,
         type_tests: Vec<TypeTest<'tcx>>,
@@ -419,17 +421,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             .map(|info| RegionDefinition::new(info.universe, info.origin))
             .collect();
 
-        let fr_static = universal_regions.fr_static;
+        let constraint_sccs =
+            outlives_constraints.add_outlives_static(&universal_regions, &definitions);
         let constraints = Frozen::freeze(outlives_constraints);
         let constraint_graph = Frozen::freeze(constraints.graph(definitions.len()));
-        let constraint_sccs = {
-            let constraint_graph = constraints.graph(definitions.len());
-            let region_graph = &constraint_graph.region_graph(&constraints, fr_static);
-            let sccs = ConstraintSccs::new_with_annotation(&region_graph, |r| {
-                RegionTracker::new(r, &definitions[r])
-            });
-            Rc::new(sccs)
-        };
 
         if cfg!(debug_assertions) {
             sccs_info(infcx, &constraint_sccs);
@@ -548,21 +543,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
                 }
 
                 NllRegionVariableOrigin::Placeholder(placeholder) => {
-                    // Each placeholder region is only visible from
-                    // its universe `ui` and its extensions. So we
-                    // can't just add it into `scc` unless the
-                    // universe of the scc can name this region.
-                    let scc_universe = self.scc_universe(scc);
-                    if scc_universe.can_name(placeholder.universe) {
-                        self.scc_values.add_element(scc, placeholder);
-                    } else {
-                        debug!(
-                            "init_free_and_bound_regions: placeholder {:?} is \
-                             not compatible with universe {:?} of its SCC {:?}",
-                            placeholder, scc_universe, scc,
-                        );
-                        self.add_incompatible_universe(scc);
-                    }
+                    self.scc_values.add_element(scc, placeholder);
                 }
 
                 NllRegionVariableOrigin::Existential { .. } => {
@@ -744,23 +725,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// (which is assured by iterating over SCCs in dependency order).
     #[instrument(skip(self), level = "debug")]
     fn compute_value_for_scc(&mut self, scc_a: ConstraintSccIndex) {
-        let constraint_sccs = self.constraint_sccs.clone();
-
         // Walk each SCC `B` such that `A: B`...
-        for &scc_b in constraint_sccs.successors(scc_a) {
+        for &scc_b in self.constraint_sccs.successors(scc_a) {
             debug!(?scc_b);
-
-            // ...and add elements from `B` into `A`. One complication
-            // arises because of universes: If `B` contains something
-            // that `A` cannot name, then `A` can only contain `B` if
-            // it outlives static.
-            if self.universe_compatible(scc_b, scc_a) {
-                // `A` can name everything that is in `B`, so just
-                // merge the bits.
-                self.scc_values.add_region(scc_a, scc_b);
-            } else {
-                self.add_incompatible_universe(scc_a);
-            }
+            self.scc_values.add_region(scc_a, scc_b);
         }
 
         // Now take member constraints into account.
@@ -814,7 +782,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
         // If the member region lives in a higher universe, we currently choose
         // the most conservative option by leaving it unchanged.
 
-        if !self.constraint_sccs().annotation(scc).universe().is_root() {
+        if !self.constraint_sccs().annotation(scc).min_universe().is_root() {
             return;
         }
 
@@ -886,35 +854,20 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// in `scc_a`. Used during constraint propagation, and only once
     /// the value of `scc_b` has been computed.
     fn universe_compatible(&self, scc_b: ConstraintSccIndex, scc_a: ConstraintSccIndex) -> bool {
-        let universe_a = self.constraint_sccs().annotation(scc_a).universe();
-        let universe_b = self.constraint_sccs().annotation(scc_b).universe();
+        let a_annotation = self.constraint_sccs().annotation(scc_a);
+        let b_annotation = self.constraint_sccs().annotation(scc_b);
+        let a_universe = a_annotation.min_universe();
 
-        // Quick check: if scc_b's declared universe is a subset of
+        // If scc_b's declared universe is a subset of
         // scc_a's declared universe (typically, both are ROOT), then
         // it cannot contain any problematic universe elements.
-        if universe_a.can_name(universe_b) {
+        if a_universe.can_name(b_annotation.min_universe()) {
             return true;
         }
 
-        // Otherwise, we have to iterate over the universe elements in
-        // B's value, and check whether all of them are nameable
-        // from universe_a
-        self.scc_values.placeholders_contained_in(scc_b).all(|p| universe_a.can_name(p.universe))
-    }
-
-    /// Extend `scc` so that it can outlive some placeholder region
-    /// from a universe it can't name; at present, the only way for
-    /// this to be true is if `scc` outlives `'static`. This is
-    /// actually stricter than necessary: ideally, we'd support bounds
-    /// like `for<'a: 'b>` that might then allow us to approximate
-    /// `'a` with `'b` and not `'static`. But it will have to do for
-    /// now.
-    fn add_incompatible_universe(&mut self, scc: ConstraintSccIndex) {
-        debug!("add_incompatible_universe(scc={:?})", scc);
-
-        let fr_static = self.universal_regions.fr_static;
-        self.scc_values.add_all_points(scc);
-        self.scc_values.add_element(scc, fr_static);
+        // Otherwise, there can be no placeholder in `b` with a too high
+        // universe index to name from `a`.
+        a_universe.can_name(b_annotation.max_placeholder_universe_reached)
     }
 
     /// Once regions have been propagated, this method is used to see
@@ -1022,7 +975,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
             "lower_bound = {:?} r_scc={:?} universe={:?}",
             lower_bound,
             r_scc,
-            self.constraint_sccs.annotation(r_scc).universe()
+            self.constraint_sccs.annotation(r_scc).min_universe()
         );
 
         // If the type test requires that `T: 'a` where `'a` is a
@@ -1539,7 +1492,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     /// The minimum universe of any variable reachable from this
     /// SCC, inside or outside of it.
     fn scc_universe(&self, scc: ConstraintSccIndex) -> UniverseIndex {
-        self.constraint_sccs().annotation(scc).universe()
+        self.constraint_sccs().annotation(scc).min_universe()
     }
     /// Checks the final value for the free region `fr` to see if it
     /// grew too large. In particular, examine what `end(X)` points
@@ -1896,6 +1849,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
 
             // This loop can be hot.
             for constraint in outgoing_edges_from_graph {
+                if matches!(constraint.category, ConstraintCategory::IllegalUniverse) {
+                    debug!("Ignoring illegal universe constraint: {constraint:?}");
+                    continue;
+                }
                 handle_constraint(constraint);
             }
 
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 67e5c8352df..c0e91ce32e3 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{GenericArgKind, GenericArgs};
 use rustc_span::Span;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::ObligationCtxt;
 
 use crate::session_diagnostics::LifetimeMismatchOpaqueParam;
@@ -285,7 +285,7 @@ impl<'tcx> InferCtxt<'tcx> {
         }
 
         if let Err(guar) =
-            check_opaque_type_parameter_valid(self.tcx, opaque_type_key, instantiated_ty.span)
+            check_opaque_type_parameter_valid(self, opaque_type_key, instantiated_ty.span)
         {
             return Ty::new_error(self.tcx, guar);
         }
@@ -294,6 +294,10 @@ impl<'tcx> InferCtxt<'tcx> {
             .remap_generic_params_to_declaration_params(opaque_type_key, self.tcx, false)
             .ty;
 
+        if let Err(e) = definition_ty.error_reported() {
+            return Ty::new_error(self.tcx, e);
+        }
+
         // `definition_ty` does not live in of the current inference context,
         // so lets make sure that we don't accidentally misuse our current `infcx`.
         match check_opaque_type_well_formed(
@@ -387,10 +391,11 @@ fn check_opaque_type_well_formed<'tcx>(
 /// [rustc-dev-guide chapter]:
 /// https://rustc-dev-guide.rust-lang.org/opaque-types-region-infer-restrictions.html
 fn check_opaque_type_parameter_valid<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    infcx: &InferCtxt<'tcx>,
     opaque_type_key: OpaqueTypeKey<'tcx>,
     span: Span,
 ) -> Result<(), ErrorGuaranteed> {
+    let tcx = infcx.tcx;
     let opaque_generics = tcx.generics_of(opaque_type_key.def_id);
     let opaque_env = LazyOpaqueTyEnv::new(tcx, opaque_type_key.def_id);
     let mut seen_params: FxIndexMap<_, Vec<_>> = FxIndexMap::default();
@@ -420,7 +425,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
 
             opaque_env.param_is_error(i)?;
 
-            return Err(tcx.dcx().emit_err(NonGenericOpaqueTypeParam {
+            return Err(infcx.dcx().emit_err(NonGenericOpaqueTypeParam {
                 ty: arg,
                 kind,
                 span,
@@ -438,7 +443,7 @@ fn check_opaque_type_parameter_valid<'tcx>(
                 .collect();
             #[allow(rustc::diagnostic_outside_of_impl)]
             #[allow(rustc::untranslatable_diagnostic)]
-            return Err(tcx
+            return Err(infcx
                 .dcx()
                 .struct_span_err(span, "non-defining opaque type use in defining scope")
                 .with_span_note(spans, format!("{descr} used multiple times"))
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 7553e3ee04f..431a704687d 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -11,8 +11,8 @@ use rustc_middle::traits::query::OutlivesBound;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
 use rustc_span::{ErrorGuaranteed, Span};
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::solve::deeply_normalize;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
 use std::rc::Rc;
 use type_op::TypeOpOutput;
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
index b777e01f7a6..6d6425b5f19 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/mod.rs
@@ -34,7 +34,7 @@ pub(super) fn generate<'mir, 'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
     body: &Body<'tcx>,
     elements: &Rc<DenseLocationMap>,
-    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
     move_data: &MoveData<'tcx>,
 ) {
     debug!("liveness::generate");
diff --git a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
index 359c4ea0eb1..eb86c8d06f1 100644
--- a/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
+++ b/compiler/rustc_borrowck/src/type_check/liveness/trace.rs
@@ -43,7 +43,7 @@ pub(super) fn trace<'mir, 'tcx>(
     typeck: &mut TypeChecker<'_, 'tcx>,
     body: &Body<'tcx>,
     elements: &Rc<DenseLocationMap>,
-    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
     move_data: &MoveData<'tcx>,
     relevant_live_locals: Vec<Local>,
     boring_locals: Vec<Local>,
@@ -101,7 +101,7 @@ pub(super) fn trace<'mir, 'tcx>(
 }
 
 /// Contextual state for the type-liveness coroutine.
-struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
+struct LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx> {
     /// Current type-checker, giving us our inference context etc.
     typeck: &'me mut TypeChecker<'typeck, 'tcx>,
 
@@ -119,7 +119,7 @@ struct LivenessContext<'me, 'typeck, 'flow, 'tcx> {
 
     /// Results of dataflow tracking which variables (and paths) have been
     /// initialized.
-    flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'flow, 'tcx>>,
+    flow_inits: &'me mut ResultsCursor<'flow, 'tcx, MaybeInitializedPlaces<'a, 'flow, 'tcx>>,
 
     /// Index indicating where each variable is assigned, used, or
     /// dropped.
@@ -131,8 +131,8 @@ struct DropData<'tcx> {
     region_constraint_data: Option<&'tcx QueryRegionConstraints<'tcx>>,
 }
 
-struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
-    cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>,
+struct LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> {
+    cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>,
 
     /// Set of points that define the current local.
     defs: BitSet<PointIndex>,
@@ -153,8 +153,8 @@ struct LivenessResults<'me, 'typeck, 'flow, 'tcx> {
     stack: Vec<PointIndex>,
 }
 
-impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
-    fn new(cx: LivenessContext<'me, 'typeck, 'flow, 'tcx>) -> Self {
+impl<'a, 'me, 'typeck, 'flow, 'tcx> LivenessResults<'a, 'me, 'typeck, 'flow, 'tcx> {
+    fn new(cx: LivenessContext<'a, 'me, 'typeck, 'flow, 'tcx>) -> Self {
         let num_points = cx.elements.num_points();
         LivenessResults {
             cx,
@@ -507,7 +507,7 @@ impl<'me, 'typeck, 'flow, 'tcx> LivenessResults<'me, 'typeck, 'flow, 'tcx> {
     }
 }
 
-impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
+impl<'tcx> LivenessContext<'_, '_, '_, '_, 'tcx> {
     /// Returns `true` if the local variable (or some part of it) is initialized at the current
     /// cursor position. Callers should call one of the `seek` methods immediately before to point
     /// the cursor to the desired location.
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 81bde14a82f..db4b5209145 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -38,6 +38,7 @@ use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_span::DUMMY_SP;
 use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
@@ -49,6 +50,7 @@ use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
 use rustc_mir_dataflow::move_paths::MoveData;
 use rustc_mir_dataflow::ResultsCursor;
 
+use crate::renumber::RegionCtxt;
 use crate::session_diagnostics::{MoveUnsized, SimdIntrinsicArgConst};
 use crate::{
     borrow_set::BorrowSet,
@@ -129,7 +131,7 @@ pub(crate) fn type_check<'mir, 'tcx>(
     location_table: &LocationTable,
     borrow_set: &BorrowSet<'tcx>,
     all_facts: &mut Option<AllFacts>,
-    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
     move_data: &MoveData<'tcx>,
     elements: &Rc<DenseLocationMap>,
     upvars: &[&ty::CapturedPlace<'tcx>],
@@ -1352,7 +1354,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 }
                 // FIXME: check the values
             }
-            TerminatorKind::Call { func, args, destination, call_source, target, .. } => {
+            TerminatorKind::Call { func, args, .. }
+            | TerminatorKind::TailCall { func, args, .. } => {
+                let call_source = match term.kind {
+                    TerminatorKind::Call { call_source, .. } => call_source,
+                    TerminatorKind::TailCall { .. } => CallSource::Normal,
+                    _ => unreachable!(),
+                };
+
                 self.check_operand(func, term_location);
                 for arg in args {
                     self.check_operand(&arg.node, term_location);
@@ -1425,7 +1434,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     );
                 }
 
-                self.check_call_dest(body, term, &sig, *destination, *target, term_location);
+                if let TerminatorKind::Call { destination, target, .. } = term.kind {
+                    self.check_call_dest(body, term, &sig, destination, target, term_location);
+                }
 
                 // The ordinary liveness rules will ensure that all
                 // regions in the type of the callee are live here. We
@@ -1443,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         .add_location(region_vid, term_location);
                 }
 
-                self.check_call_inputs(body, term, func, &sig, args, term_location, *call_source);
+                self.check_call_inputs(body, term, func, &sig, args, term_location, call_source);
             }
             TerminatorKind::Assert { cond, msg, .. } => {
                 self.check_operand(cond, term_location);
@@ -1675,6 +1686,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     span_mirbug!(self, block_data, "return on cleanup block")
                 }
             }
+            TerminatorKind::TailCall { .. } => {
+                if is_cleanup {
+                    span_mirbug!(self, block_data, "tailcall on cleanup block")
+                }
+            }
             TerminatorKind::CoroutineDrop { .. } => {
                 if is_cleanup {
                     span_mirbug!(self, block_data, "coroutine_drop in cleanup block")
@@ -2319,7 +2335,57 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         let cast_ty_from = CastTy::from_ty(ty_from);
                         let cast_ty_to = CastTy::from_ty(*ty);
                         match (cast_ty_from, cast_ty_to) {
-                            (Some(CastTy::Ptr(_)), Some(CastTy::Ptr(_))) => (),
+                            (Some(CastTy::Ptr(src)), Some(CastTy::Ptr(dst))) => {
+                                let mut normalize = |t| self.normalize(t, location);
+                                let src_tail =
+                                    tcx.struct_tail_with_normalize(src.ty, &mut normalize, || ());
+                                let dst_tail =
+                                    tcx.struct_tail_with_normalize(dst.ty, &mut normalize, || ());
+
+                                // This checks (lifetime part of) vtable validity for pointer casts,
+                                // which is irrelevant when there are aren't principal traits on both sides (aka only auto traits).
+                                //
+                                // Note that other checks (such as denying `dyn Send` -> `dyn Debug`) are in `rustc_hir_typeck`.
+                                if let ty::Dynamic(src_tty, ..) = src_tail.kind()
+                                    && let ty::Dynamic(dst_tty, ..) = dst_tail.kind()
+                                    && src_tty.principal().is_some()
+                                    && dst_tty.principal().is_some()
+                                {
+                                    // Remove auto traits.
+                                    // Auto trait checks are handled in `rustc_hir_typeck` as FCW.
+                                    let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                                        tcx.mk_poly_existential_predicates(
+                                            &src_tty.without_auto_traits().collect::<Vec<_>>(),
+                                        ),
+                                        tcx.lifetimes.re_static,
+                                        ty::Dyn,
+                                    ));
+                                    let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                                        tcx.mk_poly_existential_predicates(
+                                            &dst_tty.without_auto_traits().collect::<Vec<_>>(),
+                                        ),
+                                        tcx.lifetimes.re_static,
+                                        ty::Dyn,
+                                    ));
+
+                                    // Replace trait object lifetimes with fresh vars, to allow casts like
+                                    // `*mut dyn FnOnce() + 'a` -> `*mut dyn FnOnce() + 'static`,
+                                    let src_obj =
+                                        freshen_single_trait_object_lifetime(self.infcx, src_obj);
+                                    let dst_obj =
+                                        freshen_single_trait_object_lifetime(self.infcx, dst_obj);
+
+                                    debug!(?src_tty, ?dst_tty, ?src_obj, ?dst_obj);
+
+                                    self.eq_types(
+                                        src_obj,
+                                        dst_obj,
+                                        location.to_locations(),
+                                        ConstraintCategory::Cast { unsize_to: None },
+                                    )
+                                    .unwrap();
+                                }
+                            }
                             _ => {
                                 span_mirbug!(
                                     self,
@@ -2842,3 +2908,16 @@ impl<'tcx> TypeOp<'tcx> for InstantiateOpaqueType<'tcx> {
         Ok(output)
     }
 }
+
+fn freshen_single_trait_object_lifetime<'tcx>(
+    infcx: &BorrowckInferCtxt<'tcx>,
+    ty: Ty<'tcx>,
+) -> Ty<'tcx> {
+    let &ty::Dynamic(tty, _, dyn_kind @ ty::Dyn) = ty.kind() else { bug!("expected trait object") };
+
+    let fresh = infcx
+        .next_region_var(rustc_infer::infer::RegionVariableOrigin::MiscVariable(DUMMY_SP), || {
+            RegionCtxt::Unknown
+        });
+    infcx.tcx.mk_ty_from_kind(ty::Dynamic(tty, fresh, dyn_kind))
+}
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index b9a82046e59..02b9c2d48b1 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,8 +1,9 @@
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
-use rustc_infer::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
-use rustc_infer::infer::relate::{Relate, RelateResult, TypeRelation};
-use rustc_infer::infer::NllRegionVariableOrigin;
+use rustc_infer::infer::relate::{
+    PredicateEmittingRelation, Relate, RelateResult, StructurallyRelateAliases, TypeRelation,
+};
+use rustc_infer::infer::{InferCtxt, NllRegionVariableOrigin};
 use rustc_infer::traits::solve::Goal;
 use rustc_infer::traits::Obligation;
 use rustc_middle::mir::ConstraintCategory;
@@ -522,7 +523,7 @@ impl<'bccx, 'tcx> TypeRelation<TyCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx
     }
 }
 
-impl<'bccx, 'tcx> PredicateEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+impl<'bccx, 'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for NllTypeRelating<'_, 'bccx, 'tcx> {
     fn span(&self) -> Span {
         self.locations.span(self.type_checker.body)
     }
diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs
index 25e1f6268e0..e2de6b8b4a9 100644
--- a/compiler/rustc_borrowck/src/used_muts.rs
+++ b/compiler/rustc_borrowck/src/used_muts.rs
@@ -45,10 +45,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
 
 /// MIR visitor for collecting used mutable variables.
 /// The 'visit lifetime represents the duration of the MIR walk.
-struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'cx, 'tcx> {
+struct GatherUsedMutsVisitor<'visit, 'a, 'mir, 'infcx, 'tcx> {
     temporary_used_locals: FxIndexSet<Local>,
     never_initialized_mut_locals: &'visit mut FxIndexSet<Local>,
-    mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'cx, 'tcx>,
+    mbcx: &'visit mut MirBorrowckCtxt<'a, 'mir, 'infcx, 'tcx>,
 }
 
 impl GatherUsedMutsVisitor<'_, '_, '_, '_, '_> {
diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl
index 2d1269e1b6a..b56bfa98357 100644
--- a/compiler/rustc_builtin_macros/messages.ftl
+++ b/compiler/rustc_builtin_macros/messages.ftl
@@ -17,6 +17,9 @@ builtin_macros_asm_expected_other = expected operand, {$is_global_asm ->
     *[false] clobber_abi, options
     }, or additional template string
 
+builtin_macros_asm_expected_string_literal = expected string literal
+    .label = not a string literal
+
 builtin_macros_asm_explicit_register_name = explicit register arguments cannot have names
 
 builtin_macros_asm_mayunwind = asm labels are not allowed with the `may_unwind` option
@@ -25,6 +28,8 @@ builtin_macros_asm_modifier_invalid = asm template modifier must be a single cha
 
 builtin_macros_asm_mutually_exclusive = the `{$opt1}` and `{$opt2}` options are mutually exclusive
 
+builtin_macros_asm_no_matched_argument_name = there is no argument named `{$name}`
+
 builtin_macros_asm_noreturn = asm outputs are not allowed with the `noreturn` option
 
 builtin_macros_asm_opt_already_provided = the `{$symbol}` option was already provided
@@ -228,10 +233,16 @@ builtin_macros_only_one_argument = {$name} takes 1 argument
 
 builtin_macros_proc_macro = `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
 
+builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions = the `#[{$path}]` attribute may only be used on bare functions
+
+builtin_macros_proc_macro_attribute_only_usable_with_crate_type = the `#[{$path}]` attribute is only usable with crates of the `proc-macro` crate type
+
 builtin_macros_requires_cfg_pattern =
     macro requires a cfg-pattern as an argument
     .label = cfg-pattern required
 
+builtin_macros_source_uitls_expected_item = expected item, found `{$token}`
+
 builtin_macros_takes_no_arguments = {$name} takes no arguments
 
 builtin_macros_test_bad_fn = {$kind} functions cannot be used for tests
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 64238e81b26..dd0f9aaf221 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -390,9 +390,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
             }
             Err(opt_lit) => {
                 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
-                let mut err = p.dcx().struct_span_err(span, "expected string literal");
-                err.span_label(span, "not a string literal");
-                return Err(err);
+                return Err(p.dcx().create_err(errors::AsmExpectedStringLiteral { span }));
             }
         };
 
@@ -639,14 +637,13 @@ fn expand_preparsed_asm(
                             match args.named_args.get(&Symbol::intern(name)) {
                                 Some(&idx) => Some(idx),
                                 None => {
-                                    let msg = format!("there is no argument named `{name}`");
                                     let span = arg.position_span;
                                     ecx.dcx()
-                                        .struct_span_err(
-                                            template_span
+                                        .create_err(errors::AsmNoMatchedArgumentName {
+                                            name: name.to_owned(),
+                                            span: template_span
                                                 .from_inner(InnerSpan::new(span.start, span.end)),
-                                            msg,
-                                        )
+                                        })
                                         .emit();
                                     None
                                 }
diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs
index a98cb6f0f76..c664891dad5 100644
--- a/compiler/rustc_builtin_macros/src/assert/context.rs
+++ b/compiler/rustc_builtin_macros/src/assert/context.rs
@@ -298,7 +298,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
             // sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test.
             ExprKind::Assign(_, _, _)
             | ExprKind::AssignOp(_, _, _)
-            | ExprKind::Gen(_, _, _)
+            | ExprKind::Gen(_, _, _, _)
             | ExprKind::Await(_, _)
             | ExprKind::Block(_, _)
             | ExprKind::Break(_, _)
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index 03aff6f9633..b09975c0ba7 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -38,16 +38,14 @@ pub(crate) fn cfg_eval(
     lint_node_id: NodeId,
 ) -> Annotatable {
     let features = Some(features);
-    CfgEval { cfg: &mut StripUnconfigured { sess, features, config_tokens: true, lint_node_id } }
+    CfgEval(StripUnconfigured { sess, features, config_tokens: true, lint_node_id })
         .configure_annotatable(annotatable)
         // Since the item itself has already been configured by the `InvocationCollector`,
         // we know that fold result vector will contain exactly one element.
         .unwrap()
 }
 
-struct CfgEval<'a, 'b> {
-    cfg: &'a mut StripUnconfigured<'b>,
-}
+struct CfgEval<'a>(StripUnconfigured<'a>);
 
 fn flat_map_annotatable(
     vis: &mut impl MutVisitor,
@@ -125,9 +123,9 @@ fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool {
     res.is_break()
 }
 
-impl CfgEval<'_, '_> {
+impl CfgEval<'_> {
     fn configure<T: HasAttrs + HasTokens>(&mut self, node: T) -> Option<T> {
-        self.cfg.configure(node)
+        self.0.configure(node)
     }
 
     fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
@@ -195,8 +193,8 @@ impl CfgEval<'_, '_> {
 
         // Re-parse the tokens, setting the `capture_cfg` flag to save extra information
         // to the captured `AttrTokenStream` (specifically, we capture
-        // `AttrTokenTree::AttributesData` for all occurrences of `#[cfg]` and `#[cfg_attr]`)
-        let mut parser = Parser::new(&self.cfg.sess.psess, orig_tokens, None);
+        // `AttrTokenTree::AttrsTarget` for all occurrences of `#[cfg]` and `#[cfg_attr]`)
+        let mut parser = Parser::new(&self.0.sess.psess, orig_tokens, None);
         parser.capture_cfg = true;
         match parse_annotatable_with(&mut parser) {
             Ok(a) => annotatable = a,
@@ -212,16 +210,16 @@ impl CfgEval<'_, '_> {
     }
 }
 
-impl MutVisitor for CfgEval<'_, '_> {
+impl MutVisitor for CfgEval<'_> {
     #[instrument(level = "trace", skip(self))]
     fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
-        self.cfg.configure_expr(expr, false);
+        self.0.configure_expr(expr, false);
         mut_visit::noop_visit_expr(expr, self);
     }
 
     #[instrument(level = "trace", skip(self))]
     fn visit_method_receiver_expr(&mut self, expr: &mut P<ast::Expr>) {
-        self.cfg.configure_expr(expr, true);
+        self.0.configure_expr(expr, true);
         mut_visit::noop_visit_expr(expr, self);
     }
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/default.rs b/compiler/rustc_builtin_macros/src/deriving/default.rs
index 577523a1d5a..7a65ed97f00 100644
--- a/compiler/rustc_builtin_macros/src/deriving/default.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/default.rs
@@ -240,7 +240,7 @@ fn has_a_default_variant(item: &Annotatable) -> bool {
             if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
                 ControlFlow::Break(())
             } else {
-                // no need to subrecurse.
+                // no need to walk the variant, we are only looking for top level variants
                 ControlFlow::Continue(())
             }
         }
diff --git a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
index ea054a7e355..bbc7cd39627 100644
--- a/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/smart_ptr.rs
@@ -3,8 +3,9 @@ use std::mem::swap;
 use ast::HasAttrs;
 use rustc_ast::{
     self as ast, GenericArg, GenericBound, GenericParamKind, ItemKind, MetaItem,
-    TraitBoundModifiers,
+    TraitBoundModifiers, VariantData,
 };
+use rustc_attr as attr;
 use rustc_expand::base::{Annotatable, ExtCtxt};
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
@@ -24,11 +25,43 @@ pub fn expand_deriving_smart_ptr(
     _is_const: bool,
 ) {
     let (name_ident, generics) = if let Annotatable::Item(aitem) = item
-        && let ItemKind::Struct(_, g) = &aitem.kind
+        && let ItemKind::Struct(struct_data, g) = &aitem.kind
     {
+        let is_transparent = aitem.attrs.iter().any(|attr| {
+            attr::find_repr_attrs(cx.sess, attr)
+                .into_iter()
+                .any(|r| matches!(r, attr::ReprTransparent))
+        });
+        if !is_transparent {
+            cx.dcx()
+                .struct_span_err(
+                    span,
+                    "`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`",
+                )
+                .emit();
+            return;
+        }
+        if !matches!(
+            struct_data,
+            VariantData::Struct { fields, recovered: _ } | VariantData::Tuple(fields, _)
+                if !fields.is_empty())
+        {
+            cx.dcx()
+                .struct_span_err(
+                    span,
+                    "`SmartPointer` can only be derived on `struct`s with at least one field",
+                )
+                .emit();
+            return;
+        }
         (aitem.ident, g)
     } else {
-        cx.dcx().struct_span_err(span, "`SmartPointer` can only be derived on `struct`s").emit();
+        cx.dcx()
+            .struct_span_err(
+                span,
+                "`SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`",
+            )
+            .emit();
         return;
     };
 
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index ed2f98f2a39..49d640436c2 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -729,6 +729,14 @@ pub(crate) struct AsmExpectedComma {
 }
 
 #[derive(Diagnostic)]
+#[diag(builtin_macros_asm_expected_string_literal)]
+pub(crate) struct AsmExpectedStringLiteral {
+    #[primary_span]
+    #[label]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(builtin_macros_asm_underscore_input)]
 pub(crate) struct AsmUnderscoreInput {
     #[primary_span]
@@ -782,6 +790,14 @@ pub(crate) struct AsmNoReturn {
 }
 
 #[derive(Diagnostic)]
+#[diag(builtin_macros_asm_no_matched_argument_name)]
+pub(crate) struct AsmNoMatchedArgumentName {
+    pub(crate) name: String,
+    #[primary_span]
+    pub(crate) span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(builtin_macros_asm_mayunwind)]
 pub(crate) struct AsmMayUnwind {
     #[primary_span]
@@ -872,3 +888,27 @@ pub(crate) struct TakesNoArguments<'a> {
     pub span: Span,
     pub name: &'a str,
 }
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_proc_macro_attribute_only_be_used_on_bare_functions)]
+pub(crate) struct AttributeOnlyBeUsedOnBareFunctions<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub path: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_proc_macro_attribute_only_usable_with_crate_type)]
+pub(crate) struct AttributeOnlyUsableWithCrateType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub path: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(builtin_macros_source_uitls_expected_item)]
+pub(crate) struct ExpectedItem<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub token: &'a str,
+}
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 99d0191958d..a8a595ea579 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -214,12 +214,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
         };
 
         if !is_fn {
-            let msg = format!(
-                "the `#[{}]` attribute may only be used on bare functions",
-                pprust::path_to_string(&attr.get_normal_item().path),
-            );
-
-            self.dcx.span_err(attr.span, msg);
+            self.dcx
+                .create_err(errors::AttributeOnlyBeUsedOnBareFunctions {
+                    span: attr.span,
+                    path: &pprust::path_to_string(&attr.get_normal_item().path),
+                })
+                .emit();
             return;
         }
 
@@ -228,12 +228,12 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
         }
 
         if !self.is_proc_macro_crate {
-            let msg = format!(
-                "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type",
-                pprust::path_to_string(&attr.get_normal_item().path),
-            );
-
-            self.dcx.span_err(attr.span, msg);
+            self.dcx
+                .create_err(errors::AttributeOnlyUsableWithCrateType {
+                    span: attr.span,
+                    path: &pprust::path_to_string(&attr.get_normal_item().path),
+                })
+                .emit();
             return;
         }
 
diff --git a/compiler/rustc_builtin_macros/src/source_util.rs b/compiler/rustc_builtin_macros/src/source_util.rs
index dc1d82df0c3..44db12cf695 100644
--- a/compiler/rustc_builtin_macros/src/source_util.rs
+++ b/compiler/rustc_builtin_macros/src/source_util.rs
@@ -1,3 +1,4 @@
+use crate::errors;
 use crate::util::{
     check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr,
 };
@@ -165,9 +166,13 @@ pub(crate) fn expand_include<'cx>(
                     Ok(Some(item)) => ret.push(item),
                     Ok(None) => {
                         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().span_err(self.p.token.span, msg);
+                            self.p
+                                .dcx()
+                                .create_err(errors::ExpectedItem {
+                                    span: self.p.token.span,
+                                    token: &pprust::token_to_string(&self.p.token),
+                                })
+                                .emit();
                         }
 
                         break;
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
index b7063f35a3e..1ed6f8fc359 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/abi-cafe.yml
@@ -55,10 +55,6 @@ jobs:
       if: matrix.os == 'macos-latest' && matrix.env.TARGET_TRIPLE == 'x86_64-apple-darwin'
       run: rustup set default-host x86_64-apple-darwin
 
-    - name: Select XCode version
-      if: matrix.os == 'macos-latest'
-      run: sudo xcode-select -s /Applications/Xcode_14.3.1.app
-
     - name: Prepare dependencies
       run: ./y.sh prepare
 
diff --git a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
index 1f5a6513f63..a2ae3d63fb9 100644
--- a/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
+++ b/compiler/rustc_codegen_cranelift/.github/workflows/main.yml
@@ -66,6 +66,9 @@ jobs:
             env:
               TARGET_TRIPLE: aarch64-unknown-linux-gnu
             apt_deps: gcc-aarch64-linux-gnu qemu-user
+          - os: macos-latest
+            env:
+              TARGET_TRIPLE: aarch64-apple-darwin
           - os: ubuntu-latest
             env:
               TARGET_TRIPLE: s390x-unknown-linux-gnu
@@ -108,10 +111,6 @@ jobs:
         sudo apt-get update
         sudo apt-get install -y ${{ matrix.apt_deps }}
 
-    - name: Select XCode version
-      if: matrix.os == 'macos-latest'
-      run: sudo xcode-select -s /Applications/Xcode_14.3.1.app
-
     - name: Prepare dependencies
       run: ./y.sh prepare
 
@@ -214,6 +213,9 @@ jobs:
           - os: macos-latest
             env:
               TARGET_TRIPLE: x86_64-apple-darwin
+          - os: macos-latest
+            env:
+              TARGET_TRIPLE: aarch64-apple-darwin
           # cross-compile from Linux to Windows using mingw
           - os: ubuntu-latest
             env:
@@ -248,10 +250,6 @@ jobs:
         sudo apt-get update
         sudo apt-get install -y gcc-mingw-w64-x86-64
 
-    - name: Select XCode version
-      if: matrix.os == 'macos-latest'
-      run: sudo xcode-select -s /Applications/Xcode_14.3.1.app
-
     - name: Prepare dependencies
       run: ./y.sh prepare
 
@@ -282,7 +280,8 @@ jobs:
     runs-on: ubuntu-latest
     timeout-minutes: 10
     if: ${{ github.ref == 'refs/heads/master' }}
-    needs: [rustfmt, test, bench, dist]
+    # FIXME add the bench job back to the dependency list once rust-lang/rust#125493 gets merged
+    needs: [rustfmt, test, dist]
 
     permissions:
       contents: write # for creating the dev tag and release
diff --git a/compiler/rustc_codegen_cranelift/Cargo.lock b/compiler/rustc_codegen_cranelift/Cargo.lock
index 33fe52ddbdd..15c9e9d66fa 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.lock
+++ b/compiler/rustc_codegen_cranelift/Cargo.lock
@@ -46,18 +46,18 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "cranelift-bforest"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "79b27922a6879b5b5361d0a084cb0b1941bf109a98540addcb932da13b68bed4"
+checksum = "0b6b33d7e757a887989eb18b35712b2a67d96171ec3149d1bfb657b29b7b367c"
 dependencies = [
  "cranelift-entity",
 ]
 
 [[package]]
 name = "cranelift-codegen"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "304c455b28bf56372729acb356afbb55d622f2b0f2f7837aa5e57c138acaac4d"
+checksum = "b9acf15cb22be42d07c3b57d7856329cb228b7315d385346149df2566ad5e4aa"
 dependencies = [
  "bumpalo",
  "cranelift-bforest",
@@ -70,45 +70,46 @@ dependencies = [
  "hashbrown 0.14.3",
  "log",
  "regalloc2",
+ "rustc-hash",
  "smallvec",
  "target-lexicon",
 ]
 
 [[package]]
 name = "cranelift-codegen-meta"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1653c56b99591d07f67c5ca7f9f25888948af3f4b97186bff838d687d666f613"
+checksum = "e934d301392b73b3f8b0540391fb82465a0f179a3cee7c726482ac4727efcc97"
 dependencies = [
  "cranelift-codegen-shared",
 ]
 
 [[package]]
 name = "cranelift-codegen-shared"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5b6a9cf6b6eb820ee3f973a0db313c05dc12d370f37b4fe9630286e1672573f"
+checksum = "8afb2a2566b3d54b854dfb288b3b187f6d3d17d6f762c92898207eba302931da"
 
 [[package]]
 name = "cranelift-control"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9d06e6bf30075fb6bed9e034ec046475093392eea1aff90eb5c44c4a033d19a"
+checksum = "0100f33b704cdacd01ad66ff41f8c5030d57cbff078e2a4e49ab1822591299fa"
 dependencies = [
  "arbitrary",
 ]
 
 [[package]]
 name = "cranelift-entity"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29be04f931b73cdb9694874a295027471817f26f26d2f0ebe5454153176b6e3a"
+checksum = "a8cfdc315e5d18997093e040a8d234bea1ac1e118a716d3e30f40d449e78207b"
 
 [[package]]
 name = "cranelift-frontend"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a07fd7393041d7faa2f37426f5dc7fc04003b70988810e8c063beefeff1cd8f9"
+checksum = "0f74b84f16af2e982b0c0c72233503d9d55cbfe3865dbe807ca28dc6642a28b5"
 dependencies = [
  "cranelift-codegen",
  "log",
@@ -118,15 +119,15 @@ dependencies = [
 
 [[package]]
 name = "cranelift-isle"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f341d7938caa6dff8149dac05bb2b53fc680323826b83b4cf175ab9f5139a3c9"
+checksum = "adf306d3dde705fb94bd48082f01d38c4ededc74293a4c007805f610bf08bc6e"
 
 [[package]]
 name = "cranelift-jit"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42733555e06433f1461570e09dbd756dafc228b4dac75c597cdbdc518de07522"
+checksum = "f5c5cfb8bbd3339cd25cca30e7516ff8fe5cb1feeddde6980cc4d5ef34df97bb"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -144,9 +145,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-module"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "84950af02bb85f3da764d53a953b43bb29a732e793d4fe24637a61591be9a024"
+checksum = "7c9b0d4269b36fd858e6d8f20cd4938941186fb831488c361888cb2d6b33a9a6"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -155,9 +156,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-native"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82af6066e6448d26eeabb7aa26a43f7ff79f8217b06bade4ee6ef230aecc8880"
+checksum = "1ea0ebdef7aff4a79bcbc8b6495f31315f16b3bf311152f472eaa8d679352581"
 dependencies = [
  "cranelift-codegen",
  "libc",
@@ -166,9 +167,9 @@ dependencies = [
 
 [[package]]
 name = "cranelift-object"
-version = "0.107.0"
+version = "0.109.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00af56107039ed150391df6f753298c7b08f2b6a2e0727d216b5fa599d684d8b"
+checksum = "19e33439ec20db058bc7cc3410f9748ab1ad90a35cef713d625c736f43e3820d"
 dependencies = [
  "anyhow",
  "cranelift-codegen",
@@ -278,9 +279,9 @@ checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
 
 [[package]]
 name = "object"
-version = "0.33.0"
+version = "0.36.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8dd6c0cdf9429bce006e1362bfce61fa1bfd8c898a643ed8d2b471934701d3d"
+checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce"
 dependencies = [
  "crc32fast",
  "hashbrown 0.14.3",
@@ -410,10 +411,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
 
 [[package]]
 name = "wasmtime-jit-icache-coherence"
-version = "20.0.0"
+version = "22.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a9f93a3289057b26dc75eb84d6e60d7694f7d169c7c09597495de6e016a13ff"
+checksum = "5afe2f0499542f9a4bcfa1b55bfdda803b6ade4e7c93c6b99e0f39dba44b0a91"
 dependencies = [
+ "anyhow",
  "cfg-if",
  "libc",
  "windows-sys",
diff --git a/compiler/rustc_codegen_cranelift/Cargo.toml b/compiler/rustc_codegen_cranelift/Cargo.toml
index 2015cdbcc2a..2969a6cf6ec 100644
--- a/compiler/rustc_codegen_cranelift/Cargo.toml
+++ b/compiler/rustc_codegen_cranelift/Cargo.toml
@@ -8,15 +8,15 @@ crate-type = ["dylib"]
 
 [dependencies]
 # These have to be in sync with each other
-cranelift-codegen = { version = "0.107.0", default-features = false, features = ["std", "unwind", "all-arch"] }
-cranelift-frontend = { version = "0.107.0" }
-cranelift-module = { version = "0.107.0" }
-cranelift-native = { version = "0.107.0" }
-cranelift-jit = { version = "0.107.0", optional = true }
-cranelift-object = { version = "0.107.0" }
+cranelift-codegen = { version = "0.109.0", default-features = false, features = ["std", "unwind", "all-arch"] }
+cranelift-frontend = { version = "0.109.0" }
+cranelift-module = { version = "0.109.0" }
+cranelift-native = { version = "0.109.0" }
+cranelift-jit = { version = "0.109.0", optional = true }
+cranelift-object = { version = "0.109.0" }
 target-lexicon = "0.12.0"
 gimli = { version = "0.28", default-features = false, features = ["write"]}
-object = { version = "0.33", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
+object = { version = "0.36", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
 
 indexmap = "2.0.0"
 libloading = { version = "0.8.0", optional = true }
diff --git a/compiler/rustc_codegen_cranelift/Readme.md b/compiler/rustc_codegen_cranelift/Readme.md
index 00ea15cb38c..eb21e027dd0 100644
--- a/compiler/rustc_codegen_cranelift/Readme.md
+++ b/compiler/rustc_codegen_cranelift/Readme.md
@@ -70,7 +70,7 @@ For more docs on how to build and test see [build_system/usage.txt](build_system
 |FreeBSD|✅[^no-rustup]|❓|❓|❓|
 |AIX|❌[^xcoff]|N/A|N/A|❌[^xcoff]|
 |Other unixes|❓|❓|❓|❓|
-|macOS|✅|❌[^apple-silicon]|N/A|N/A|
+|macOS|✅|✅[^no-rustup]|N/A|N/A|
 |Windows|✅[^no-rustup]|❌|N/A|N/A|
 
 ✅: Fully supported and tested
@@ -80,7 +80,6 @@ For more docs on how to build and test see [build_system/usage.txt](build_system
 Not all targets are available as rustup component for nightly. See notes in the platform support matrix.
 
 [^xcoff]: XCOFF object file format is not supported.
-[^apple-silicon]: Tracked in [#1248](https://github.com/rust-lang/rustc_codegen_cranelift/issues/1248).
 [^no-rustup]: Not available as rustup component for nightly. You can build it yourself.
 
 ## Usage
diff --git a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
index 196ff8fda75..dfbe0f51e7b 100644
--- a/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/build_sysroot.rs
@@ -267,10 +267,6 @@ fn build_clif_sysroot_for_triple(
             prefix.to_str().unwrap()
         ));
     }
-    rustflags.push("-Zunstable-options".to_owned());
-    for (name, values) in EXTRA_CHECK_CFGS {
-        rustflags.push(check_cfg_arg(name, *values));
-    }
     compiler.rustflags.extend(rustflags);
     let mut build_cmd = STANDARD_LIBRARY.build(&compiler, dirs);
     if channel == "release" {
@@ -330,34 +326,3 @@ fn build_rtstartup(dirs: &Dirs, compiler: &Compiler) -> Option<SysrootTarget> {
 
     Some(target_libs)
 }
-
-// Copied from https://github.com/rust-lang/rust/blob/4fd98a4b1b100f5329c6efae18031791f64372d2/src/bootstrap/src/utils/helpers.rs#L569-L585
-/// Create a `--check-cfg` argument invocation for a given name
-/// and it's values.
-fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String {
-    // Creating a string of the values by concatenating each value:
-    // ',values("tvos","watchos")' or '' (nothing) when there are no values.
-    let next = match values {
-        Some(values) => {
-            let mut tmp = values.iter().flat_map(|val| [",", "\"", val, "\""]).collect::<String>();
-
-            tmp.insert_str(1, "values(");
-            tmp.push(')');
-            tmp
-        }
-        None => "".to_string(),
-    };
-    format!("--check-cfg=cfg({name}{next})")
-}
-
-const EXTRA_CHECK_CFGS: &[(&str, Option<&[&str]>)] = &[
-    ("bootstrap", None),
-    ("stdarch_intel_sde", None),
-    ("no_fp_fmt_parse", None),
-    ("no_global_oom_handling", None),
-    ("no_rc", None),
-    ("no_sync", None),
-    ("netbsd10", None),
-    ("backtrace_in_libstd", None),
-    ("target_arch", Some(&["xtensa"])),
-];
diff --git a/compiler/rustc_codegen_cranelift/build_system/tests.rs b/compiler/rustc_codegen_cranelift/build_system/tests.rs
index 278f334796a..790d9cbd9fc 100644
--- a/compiler/rustc_codegen_cranelift/build_system/tests.rs
+++ b/compiler/rustc_codegen_cranelift/build_system/tests.rs
@@ -329,7 +329,6 @@ pub(crate) fn run_tests(
 struct TestRunner<'a> {
     is_native: bool,
     jit_supported: bool,
-    use_unstable_features: bool,
     skip_tests: &'a [&'a str],
     dirs: Dirs,
     target_compiler: Compiler,
@@ -361,15 +360,7 @@ impl<'a> TestRunner<'a> {
             && target_compiler.triple.contains("x86_64")
             && !target_compiler.triple.contains("windows");
 
-        Self {
-            is_native,
-            jit_supported,
-            use_unstable_features,
-            skip_tests,
-            dirs,
-            target_compiler,
-            stdlib_source,
-        }
+        Self { is_native, jit_supported, skip_tests, dirs, target_compiler, stdlib_source }
     }
 
     fn run_testsuite(&self, tests: &[TestCase]) {
@@ -393,31 +384,13 @@ impl<'a> TestRunner<'a> {
             match *cmd {
                 TestCaseCmd::Custom { func } => func(self),
                 TestCaseCmd::BuildLib { source, crate_types } => {
-                    if self.use_unstable_features {
-                        self.run_rustc([source, "--crate-type", crate_types]);
-                    } else {
-                        self.run_rustc([
-                            source,
-                            "--crate-type",
-                            crate_types,
-                            "--cfg",
-                            "no_unstable_features",
-                        ]);
-                    }
+                    self.run_rustc([source, "--crate-type", crate_types]);
                 }
                 TestCaseCmd::BuildBin { source } => {
-                    if self.use_unstable_features {
-                        self.run_rustc([source]);
-                    } else {
-                        self.run_rustc([source, "--cfg", "no_unstable_features"]);
-                    }
+                    self.run_rustc([source]);
                 }
                 TestCaseCmd::BuildBinAndRun { source, args } => {
-                    if self.use_unstable_features {
-                        self.run_rustc([source]);
-                    } else {
-                        self.run_rustc([source, "--cfg", "no_unstable_features"]);
-                    }
+                    self.run_rustc([source]);
                     self.run_out_command(
                         source.split('/').last().unwrap().split('.').next().unwrap(),
                         args,
@@ -472,7 +445,6 @@ impl<'a> TestRunner<'a> {
         cmd.arg(&self.target_compiler.triple);
         cmd.arg("-Cpanic=abort");
         cmd.arg("-Zunstable-options");
-        cmd.arg("--check-cfg=cfg(no_unstable_features)");
         cmd.arg("--check-cfg=cfg(jit)");
         cmd.args(args);
         cmd
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 aab20f67248..7d361a9ab2b 100644
--- a/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
+++ b/compiler/rustc_codegen_cranelift/example/mini_core_hello_world.rs
@@ -333,12 +333,7 @@ fn main() {
     #[cfg(all(not(jit), not(all(windows, target_env = "gnu"))))]
     test_tls();
 
-    #[cfg(all(
-        not(jit),
-        not(no_unstable_features),
-        target_arch = "x86_64",
-        any(target_os = "linux", target_os = "macos")
-    ))]
+    #[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
     unsafe {
         global_asm_test();
         naked_test();
@@ -367,17 +362,12 @@ fn stack_val_align() {
     assert_eq!(&a as *const Foo as usize % 8192, 0);
 }
 
-#[cfg(all(
-    not(jit),
-    not(no_unstable_features),
-    target_arch = "x86_64",
-    any(target_os = "linux", target_os = "macos")
-))]
+#[cfg(all(not(jit), target_arch = "x86_64", any(target_os = "linux", target_os = "macos")))]
 extern "C" {
     fn global_asm_test();
 }
 
-#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "linux"))]
+#[cfg(all(not(jit), target_arch = "x86_64", target_os = "linux"))]
 global_asm! {
     "
     .global global_asm_test
@@ -387,7 +377,7 @@ global_asm! {
     "
 }
 
-#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64", target_os = "macos"))]
+#[cfg(all(not(jit), target_arch = "x86_64", target_os = "macos"))]
 global_asm! {
     "
     .global _global_asm_test
@@ -397,7 +387,7 @@ global_asm! {
     "
 }
 
-#[cfg(all(not(jit), not(no_unstable_features), target_arch = "x86_64"))]
+#[cfg(all(not(jit), target_arch = "x86_64"))]
 #[naked]
 extern "C" fn naked_test() {
     unsafe {
diff --git a/compiler/rustc_codegen_cranelift/example/std_example.rs b/compiler/rustc_codegen_cranelift/example/std_example.rs
index 7347b2e7789..6cedd84adfe 100644
--- a/compiler/rustc_codegen_cranelift/example/std_example.rs
+++ b/compiler/rustc_codegen_cranelift/example/std_example.rs
@@ -251,6 +251,9 @@ unsafe fn test_simd() {
     test_mm_add_epi8();
     test_mm_add_pd();
     test_mm_cvtepi8_epi16();
+    #[cfg(not(jit))]
+    test_mm_cvtps_epi32();
+    test_mm_cvttps_epi32();
     test_mm_cvtsi128_si64();
 
     test_mm_extract_epi8();
@@ -476,6 +479,41 @@ unsafe fn test_mm256_permutevar8x32_epi32() {
     assert_eq_m256i(r, e);
 }
 
+#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "avx2")]
+#[cfg(not(jit))]
+unsafe fn test_mm_cvtps_epi32() {
+    let floats: [f32; 4] = [1.5, -2.5, i32::MAX as f32 + 1.0, f32::NAN];
+
+    let float_vec = _mm_loadu_ps(floats.as_ptr());
+    let int_vec = _mm_cvtps_epi32(float_vec);
+
+    let mut ints: [i32; 4] = [0; 4];
+    _mm_storeu_si128(ints.as_mut_ptr() as *mut __m128i, int_vec);
+
+    // this is very different from `floats.map(|f| f as i32)`!
+    let expected_ints: [i32; 4] = [2, -2, i32::MIN, i32::MIN];
+
+    assert_eq!(ints, expected_ints);
+}
+
+#[cfg(target_arch = "x86_64")]
+#[target_feature(enable = "avx2")]
+unsafe fn test_mm_cvttps_epi32() {
+    let floats: [f32; 4] = [1.5, -2.5, i32::MAX as f32 + 1.0, f32::NAN];
+
+    let float_vec = _mm_loadu_ps(floats.as_ptr());
+    let int_vec = _mm_cvttps_epi32(float_vec);
+
+    let mut ints: [i32; 4] = [0; 4];
+    _mm_storeu_si128(ints.as_mut_ptr() as *mut __m128i, int_vec);
+
+    // this is very different from `floats.map(|f| f as i32)`!
+    let expected_ints: [i32; 4] = [1, -2, i32::MIN, i32::MIN];
+
+    assert_eq!(ints, expected_ints);
+}
+
 fn test_checked_mul() {
     let u: Option<u8> = u8::from_str_radix("1000", 10).ok();
     assert_eq!(u, None);
diff --git a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
index c8c7b45bc9a..9ea53e8f848 100644
--- a/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
+++ b/compiler/rustc_codegen_cranelift/patches/stdlib-lock.toml
@@ -4,12 +4,12 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.21.0"
+version = "0.22.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
+checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678"
 dependencies = [
  "compiler_builtins",
- "gimli",
+ "gimli 0.29.0",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
 ]
@@ -134,6 +134,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "gimli"
+version = "0.29.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd"
+dependencies = [
+ "compiler_builtins",
+ "rustc-std-workspace-alloc",
+ "rustc-std-workspace-core",
+]
+
+[[package]]
 name = "hashbrown"
 version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -147,9 +158,9 @@ dependencies = [
 
 [[package]]
 name = "hermit-abi"
-version = "0.3.9"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
+checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-alloc",
@@ -189,9 +200,9 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.32.1"
+version = "0.36.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
+checksum = "576dfe1fc8f9df304abb159d767a29d0476f7750fbf8aa7ad07816004a207434"
 dependencies = [
  "compiler_builtins",
  "memchr",
@@ -286,9 +297,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-demangle"
-version = "0.1.23"
+version = "0.1.24"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
+checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
 dependencies = [
  "compiler_builtins",
  "rustc-std-workspace-core",
@@ -396,8 +407,6 @@ dependencies = [
  "core",
  "getopts",
  "libc",
- "panic_abort",
- "panic_unwind",
  "std",
 ]
 
@@ -430,7 +439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b"
 dependencies = [
  "compiler_builtins",
- "gimli",
+ "gimli 0.28.1",
  "rustc-std-workspace-core",
 ]
 
diff --git a/compiler/rustc_codegen_cranelift/rust-toolchain b/compiler/rustc_codegen_cranelift/rust-toolchain
index a2ba79cbe90..cfa91744a0e 100644
--- a/compiler/rustc_codegen_cranelift/rust-toolchain
+++ b/compiler/rustc_codegen_cranelift/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-05-13"
+channel = "nightly-2024-06-30"
 components = ["rust-src", "rustc-dev", "llvm-tools"]
diff --git a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
index 689cda21643..c1b7e4b0e07 100755
--- a/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
+++ b/compiler/rustc_codegen_cranelift/scripts/test_rustc_tests.sh
@@ -36,9 +36,8 @@ rm tests/ui/parser/unclosed-delimiter-in-dep.rs # submodule contains //~ERROR
 rm tests/ui/asm/x86_64/evex512-implicit-feature.rs # unimplemented AVX512 x86 vendor intrinsic
 
 # exotic linkages
-rm tests/ui/issues/issue-33992.rs # unsupported linkages
-rm tests/incremental/hashes/function_interfaces.rs # same
-rm tests/incremental/hashes/statics.rs # same
+rm tests/incremental/hashes/function_interfaces.rs
+rm tests/incremental/hashes/statics.rs
 
 # variadic arguments
 rm tests/ui/abi/mir/mir_codegen_calls_variadic.rs # requires float varargs
@@ -60,13 +59,20 @@ rm tests/ui/asm/x86_64/goto.rs # inline asm labels not supported
 
 # requires LTO
 rm -r tests/run-make/cdylib
-rm -r tests/run-make/issue-14500
-rm -r tests/run-make/issue-64153
 rm -r tests/run-make/codegen-options-parsing
 rm -r tests/run-make/lto-*
 rm -r tests/run-make/reproducible-build-2
 rm -r tests/run-make/issue-109934-lto-debuginfo
 rm -r tests/run-make/no-builtins-lto
+rm -r tests/run-make/reachable-extern-fn-available-lto
+
+# coverage instrumentation
+rm tests/ui/consts/precise-drop-with-coverage.rs
+rm tests/ui/issues/issue-85461.rs
+rm -r tests/ui/instrument-coverage/
+
+# missing f16/f128 support
+rm tests/ui/half-open-range-patterns/half-open-range-pats-semantics.rs
 
 # optimization tests
 # ==================
@@ -74,6 +80,7 @@ rm tests/ui/codegen/issue-28950.rs # depends on stack size optimizations
 rm tests/ui/codegen/init-large-type.rs # same
 rm tests/ui/issues/issue-40883.rs # same
 rm -r tests/run-make/fmt-write-bloat/ # tests an optimization
+rm tests/ui/statics/const_generics.rs # same
 
 # backend specific tests
 # ======================
@@ -85,6 +92,7 @@ rm -r tests/run-make/sepcomp-cci-copies # same
 rm -r tests/run-make/volatile-intrinsics # same
 rm -r tests/run-make/llvm-ident # same
 rm -r tests/run-make/no-builtins-attribute # same
+rm -r tests/run-make/pgo-gen-no-imp-symbols # same
 rm tests/ui/abi/stack-protector.rs # requires stack protector support
 rm -r tests/run-make/emit-stack-sizes # requires support for -Z emit-stack-sizes
 rm -r tests/run-make/optimization-remarks-dir # remarks are LLVM specific
@@ -93,13 +101,14 @@ rm -r tests/run-make/print-to-output # requires --print relocation-models
 # requires asm, llvm-ir and/or llvm-bc emit support
 # =============================================
 rm -r tests/run-make/emit-named-files
-rm -r tests/run-make/issue-30063
 rm -r tests/run-make/multiple-emits
 rm -r tests/run-make/output-type-permutations
 rm -r tests/run-make/emit-to-stdout
 rm -r tests/run-make/compressed-debuginfo
 rm -r tests/run-make/symbols-include-type-name
-
+rm -r tests/run-make/notify-all-emit-artifacts
+rm -r tests/run-make/reset-codegen-1
+rm -r tests/run-make/inline-always-many-cgu
 
 # giving different but possibly correct results
 # =============================================
@@ -118,6 +127,7 @@ rm -r tests/run-make/compiler-builtins # Expects lib/rustlib/src/rust to contain
 # ============
 rm -r tests/run-make/extern-fn-explicit-align # argument alignment not yet supported
 rm -r tests/run-make/panic-abort-eh_frame # .eh_frame emitted with panic=abort
+rm tests/ui/deprecation/deprecated_inline_threshold.rs # missing deprecation warning for -Cinline-threshold
 
 # bugs in the test suite
 # ======================
@@ -148,12 +158,12 @@ index 9607ff02f96..b7d97caf9a2 100644
 --- a/src/tools/run-make-support/src/rustdoc.rs
 +++ b/src/tools/run-make-support/src/rustdoc.rs
 @@ -34,8 +34,6 @@ pub fn bare() -> Self {
-     /// Construct a \`rustdoc\` invocation with \`-L \$(TARGET_RPATH_DIR)\` set.
+     #[track_caller]
      pub fn new() -> Self {
          let mut cmd = setup_common();
--        let target_rpath_dir = env::var_os("TARGET_RPATH_DIR").unwrap();
+-        let target_rpath_dir = env_var_os("TARGET_RPATH_DIR");
 -        cmd.arg(format!("-L{}", target_rpath_dir.to_string_lossy()));
-         Self { cmd, stdin: None }
+         Self { cmd }
      }
 
 EOF
diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
index 695dbaf2804..9dc94ab33ea 100644
--- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs
@@ -5,8 +5,9 @@ mod pass_mode;
 mod returning;
 
 use std::borrow::Cow;
+use std::mem;
 
-use cranelift_codegen::ir::SigRef;
+use cranelift_codegen::ir::{ArgumentPurpose, SigRef};
 use cranelift_codegen::isa::CallConv;
 use cranelift_module::ModuleError;
 use rustc_codegen_ssa::errors::CompilerBuiltinsCannotCall;
@@ -17,7 +18,7 @@ use rustc_middle::ty::TypeVisitableExt;
 use rustc_monomorphize::is_call_from_compiler_builtins_to_upstream_monomorphization;
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
-use rustc_target::abi::call::{Conv, FnAbi};
+use rustc_target::abi::call::{Conv, FnAbi, PassMode};
 use rustc_target::spec::abi::Abi;
 
 use self::pass_mode::*;
@@ -370,9 +371,14 @@ pub(crate) fn codegen_terminator_call<'tcx>(
 
     // Handle special calls like intrinsics and empty drop glue.
     let instance = if let ty::FnDef(def_id, fn_args) = *func.layout().ty.kind() {
-        let instance =
-            ty::Instance::expect_resolve(fx.tcx, ty::ParamEnv::reveal_all(), def_id, fn_args)
-                .polymorphize(fx.tcx);
+        let instance = ty::Instance::expect_resolve(
+            fx.tcx,
+            ty::ParamEnv::reveal_all(),
+            def_id,
+            fn_args,
+            source_info.span,
+        )
+        .polymorphize(fx.tcx);
 
         if is_call_from_compiler_builtins_to_upstream_monomorphization(fx.tcx, instance) {
             if target.is_some() {
@@ -487,6 +493,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
     let args = args;
     assert_eq!(fn_abi.args.len(), args.len());
 
+    #[derive(Copy, Clone)]
     enum CallTarget {
         Direct(FuncRef),
         Indirect(SigRef, Value),
@@ -532,7 +539,7 @@ pub(crate) fn codegen_terminator_call<'tcx>(
     };
 
     self::returning::codegen_with_call_return_arg(fx, &fn_abi.ret, ret_place, |fx, return_ptr| {
-        let call_args = return_ptr
+        let mut call_args = return_ptr
             .into_iter()
             .chain(first_arg_override.into_iter())
             .chain(
@@ -545,47 +552,118 @@ pub(crate) fn codegen_terminator_call<'tcx>(
             )
             .collect::<Vec<Value>>();
 
-        let call_inst = match func_ref {
+        // FIXME: Find a cleaner way to support varargs.
+        if fn_abi.c_variadic {
+            adjust_call_for_c_variadic(fx, &fn_abi, source_info, func_ref, &mut call_args);
+        }
+
+        match func_ref {
             CallTarget::Direct(func_ref) => fx.bcx.ins().call(func_ref, &call_args),
             CallTarget::Indirect(sig, func_ptr) => {
                 fx.bcx.ins().call_indirect(sig, func_ptr, &call_args)
             }
+        }
+    });
+
+    if let Some(dest) = target {
+        let ret_block = fx.get_block(dest);
+        fx.bcx.ins().jump(ret_block, &[]);
+    } else {
+        fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
+    }
+
+    fn adjust_call_for_c_variadic<'tcx>(
+        fx: &mut FunctionCx<'_, '_, 'tcx>,
+        fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
+        source_info: mir::SourceInfo,
+        target: CallTarget,
+        call_args: &mut Vec<Value>,
+    ) {
+        if fn_abi.conv != Conv::C {
+            fx.tcx.dcx().span_fatal(
+                source_info.span,
+                format!("Variadic call for non-C abi {:?}", fn_abi.conv),
+            );
+        }
+        let sig_ref = match target {
+            CallTarget::Direct(func_ref) => fx.bcx.func.dfg.ext_funcs[func_ref].signature,
+            CallTarget::Indirect(sig_ref, _) => sig_ref,
         };
+        // `mem::take()` the `params` so that `fx.bcx` can be used below.
+        let mut abi_params = mem::take(&mut fx.bcx.func.dfg.signatures[sig_ref].params);
+
+        // Recalculate the parameters in the signature to ensure the signature contains the variadic arguments.
+        let has_return_arg = matches!(fn_abi.ret.mode, PassMode::Indirect { .. });
+        // Drop everything except the return argument (if there is one).
+        abi_params.truncate(if has_return_arg { 1 } else { 0 });
+        // Add the fixed arguments.
+        abi_params.extend(
+            fn_abi.args[..fn_abi.fixed_count as usize]
+                .iter()
+                .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()),
+        );
+        let fixed_arg_count = abi_params.len();
+        // Add the variadic arguments.
+        abi_params.extend(
+            fn_abi.args[fn_abi.fixed_count as usize..]
+                .iter()
+                .flat_map(|arg_abi| arg_abi.get_abi_param(fx.tcx).into_iter()),
+        );
 
-        // FIXME find a cleaner way to support varargs
-        if fn_sig.c_variadic() {
-            if !matches!(fn_sig.abi(), Abi::C { .. }) {
+        if fx.tcx.sess.target.is_like_osx && fx.tcx.sess.target.arch == "aarch64" {
+            // Add any padding arguments needed for Apple AArch64.
+            // There's no need to pad the argument list unless variadic arguments are actually being
+            // passed.
+            if abi_params.len() > fixed_arg_count {
+                // 128-bit integers take 2 registers, and everything else takes 1.
+                // FIXME: Add support for non-integer types
+                // This relies on the checks below to ensure all arguments are integer types and
+                // that the ABI is "C".
+                // The return argument isn't counted as it goes in its own dedicated register.
+                let integer_registers_used: usize = abi_params
+                    [if has_return_arg { 1 } else { 0 }..fixed_arg_count]
+                    .iter()
+                    .map(|arg| if arg.value_type.bits() == 128 { 2 } else { 1 })
+                    .sum();
+                // The ABI uses 8 registers before it starts pushing arguments to the stack. Pad out
+                // the registers if needed to ensure the variadic arguments are passed on the stack.
+                if integer_registers_used < 8 {
+                    abi_params.splice(
+                        fixed_arg_count..fixed_arg_count,
+                        (integer_registers_used..8).map(|_| AbiParam::new(types::I64)),
+                    );
+                    call_args.splice(
+                        fixed_arg_count..fixed_arg_count,
+                        (integer_registers_used..8).map(|_| fx.bcx.ins().iconst(types::I64, 0)),
+                    );
+                }
+            }
+
+            // `StructArgument` is not currently used by the `aarch64` ABI, and is therefore not
+            // handled when calculating how many padding arguments to use. Assert that this remains
+            // the case.
+            assert!(abi_params.iter().all(|param| matches!(
+                param.purpose,
+                // The only purposes used are `Normal` and `StructReturn`.
+                ArgumentPurpose::Normal | ArgumentPurpose::StructReturn
+            )));
+        }
+
+        // Check all parameters are integers.
+        for param in abi_params.iter() {
+            if !param.value_type.is_int() {
+                // FIXME: Set %al to upperbound on float args once floats are supported.
                 fx.tcx.dcx().span_fatal(
                     source_info.span,
-                    format!("Variadic call for non-C abi {:?}", fn_sig.abi()),
+                    format!("Non int ty {:?} for variadic call", param.value_type),
                 );
             }
-            let sig_ref = fx.bcx.func.dfg.call_signature(call_inst).unwrap();
-            let abi_params = call_args
-                .into_iter()
-                .map(|arg| {
-                    let ty = fx.bcx.func.dfg.value_type(arg);
-                    if !ty.is_int() {
-                        // FIXME set %al to upperbound on float args once floats are supported
-                        fx.tcx.dcx().span_fatal(
-                            source_info.span,
-                            format!("Non int ty {:?} for variadic call", ty),
-                        );
-                    }
-                    AbiParam::new(ty)
-                })
-                .collect::<Vec<AbiParam>>();
-            fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
         }
 
-        call_inst
-    });
+        assert_eq!(abi_params.len(), call_args.len());
 
-    if let Some(dest) = target {
-        let ret_block = fx.get_block(dest);
-        fx.bcx.ins().jump(ret_block, &[]);
-    } else {
-        fx.bcx.ins().trap(TrapCode::UnreachableCodeReached);
+        // Put the `AbiParam`s back in the signature.
+        fx.bcx.func.dfg.signatures[sig_ref].params = abi_params;
     }
 }
 
diff --git a/compiler/rustc_codegen_cranelift/src/allocator.rs b/compiler/rustc_codegen_cranelift/src/allocator.rs
index e8af3e8c255..b4a3825e996 100644
--- a/compiler/rustc_codegen_cranelift/src/allocator.rs
+++ b/compiler/rustc_codegen_cranelift/src/allocator.rs
@@ -11,15 +11,10 @@ use rustc_session::config::OomStrategy;
 use crate::prelude::*;
 
 /// Returns whether an allocator shim was created
-pub(crate) fn codegen(
-    tcx: TyCtxt<'_>,
-    module: &mut impl Module,
-    unwind_context: &mut UnwindContext,
-) -> bool {
+pub(crate) fn codegen(tcx: TyCtxt<'_>, module: &mut dyn Module) -> bool {
     let Some(kind) = allocator_kind_for_codegen(tcx) else { return false };
     codegen_inner(
         module,
-        unwind_context,
         kind,
         tcx.alloc_error_handler_kind(()).unwrap(),
         tcx.sess.opts.unstable_opts.oom,
@@ -28,8 +23,7 @@ pub(crate) fn codegen(
 }
 
 fn codegen_inner(
-    module: &mut impl Module,
-    unwind_context: &mut UnwindContext,
+    module: &mut dyn Module,
     kind: AllocatorKind,
     alloc_error_handler_kind: AllocatorKind,
     oom_strategy: OomStrategy,
@@ -67,7 +61,6 @@ fn codegen_inner(
             };
             crate::common::create_wrapper_function(
                 module,
-                unwind_context,
                 sig,
                 &global_fn_name(method.name),
                 &default_fn_name(method.name),
@@ -82,7 +75,6 @@ fn codegen_inner(
     };
     crate::common::create_wrapper_function(
         module,
-        unwind_context,
         sig,
         "__rust_alloc_error_handler",
         &alloc_error_handler_name(alloc_error_handler_kind),
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index b117dc496c2..5adbbb09ac8 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -249,9 +249,7 @@ pub(crate) fn compile_fn(
     }
 
     // Define debuginfo for function
-    let isa = module.isa();
     let debug_context = &mut cx.debug_context;
-    let unwind_context = &mut cx.unwind_context;
     cx.profiler.generic_activity("generate debug info").run(|| {
         if let Some(debug_context) = debug_context {
             codegened_func.func_debug_cx.unwrap().finalize(
@@ -260,7 +258,6 @@ pub(crate) fn compile_fn(
                 context,
             );
         }
-        unwind_context.add_function(codegened_func.func_id, &context, isa);
     });
 }
 
@@ -494,6 +491,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
                     )
                 });
             }
+            // FIXME(explicit_tail_calls): add support for tail calls to the cranelift backend, once cranelift supports tail calls
+            TerminatorKind::TailCall { fn_span, .. } => span_bug!(
+                *fn_span,
+                "tail calls are not yet supported in `rustc_codegen_cranelift` backend"
+            ),
             TerminatorKind::InlineAsm {
                 template,
                 operands,
@@ -909,7 +911,7 @@ fn codegen_stmt<'tcx>(
         | StatementKind::PlaceMention(..)
         | StatementKind::AscribeUserType(..) => {}
 
-        StatementKind::Coverage { .. } => fx.tcx.dcx().fatal("-Zcoverage is unimplemented"),
+        StatementKind::Coverage { .. } => unreachable!(),
         StatementKind::Intrinsic(ref intrinsic) => match &**intrinsic {
             // We ignore `assume` intrinsics, they are only useful for optimizations
             NonDivergingIntrinsic::Assume(_) => {}
diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs
index 21d0cd2d30f..09317139936 100644
--- a/compiler/rustc_codegen_cranelift/src/common.rs
+++ b/compiler/rustc_codegen_cranelift/src/common.rs
@@ -247,7 +247,6 @@ pub(crate) fn type_sign(ty: Ty<'_>) -> bool {
 
 pub(crate) fn create_wrapper_function(
     module: &mut dyn Module,
-    unwind_context: &mut UnwindContext,
     sig: Signature,
     wrapper_name: &str,
     callee_name: &str,
@@ -280,7 +279,6 @@ pub(crate) fn create_wrapper_function(
         bcx.finalize();
     }
     module.define_function(wrapper_func_id, &mut ctx).unwrap();
-    unwind_context.add_function(wrapper_func_id, &ctx, module.isa());
 }
 
 pub(crate) struct FunctionCx<'m, 'clif, 'tcx: 'm> {
@@ -395,6 +393,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
                 // FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets
                 // a way to specify stack slot alignment.
                 size: (size + abi_align - 1) / abi_align * abi_align,
+                align_shift: 4,
             });
             Pointer::stack_slot(stack_slot)
         } else {
@@ -405,6 +404,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> {
                 // FIXME Don't force the size to a multiple of <abi_align> bytes once Cranelift gets
                 // a way to specify stack slot alignment.
                 size: (size + align) / abi_align * abi_align,
+                align_shift: 4,
             });
             let base_ptr = self.bcx.ins().stack_addr(self.pointer_type, stack_slot, 0);
             let misalign_offset = self.bcx.ins().urem_imm(base_ptr, i64::from(align));
diff --git a/compiler/rustc_codegen_cranelift/src/constant.rs b/compiler/rustc_codegen_cranelift/src/constant.rs
index 87c5da3b7c3..fd34ed88c0b 100644
--- a/compiler/rustc_codegen_cranelift/src/constant.rs
+++ b/compiler/rustc_codegen_cranelift/src/constant.rs
@@ -155,7 +155,7 @@ pub(crate) fn codegen_const_value<'tcx>(
                             fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
                         }
                     }
-                    GlobalAlloc::Function(instance) => {
+                    GlobalAlloc::Function { instance, .. } => {
                         let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
                         let local_func_id =
                             fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
@@ -351,7 +351,9 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
             TodoItem::Alloc(alloc_id) => {
                 let alloc = match tcx.global_alloc(alloc_id) {
                     GlobalAlloc::Memory(alloc) => alloc,
-                    GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => {
+                    GlobalAlloc::Function { .. }
+                    | GlobalAlloc::Static(_)
+                    | GlobalAlloc::VTable(..) => {
                         unreachable!()
                     }
                 };
@@ -415,7 +417,7 @@ fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut Constant
 
             let reloc_target_alloc = tcx.global_alloc(alloc_id);
             let data_id = match reloc_target_alloc {
-                GlobalAlloc::Function(instance) => {
+                GlobalAlloc::Function { instance, .. } => {
                     assert_eq!(addend, 0);
                     let func_id =
                         crate::abi::import_function(tcx, module, instance.polymorphize(tcx));
@@ -565,6 +567,7 @@ pub(crate) fn mir_operand_get_const_val<'tcx>(
                     {
                         return None;
                     }
+                    TerminatorKind::TailCall { .. } => return None,
                     TerminatorKind::Call { .. } => {}
                 }
             }
diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
index dcafac21bc7..763d9a48407 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs
@@ -26,6 +26,7 @@ use rustc_session::Session;
 use crate::concurrency_limiter::{ConcurrencyLimiter, ConcurrencyLimiterToken};
 use crate::debuginfo::TypeDebugContext;
 use crate::global_asm::GlobalAsmConfig;
+use crate::unwind_module::UnwindModule;
 use crate::{prelude::*, BackendConfig};
 
 struct ModuleCodegenResult {
@@ -318,7 +319,11 @@ fn produce_final_output_artifacts(
     // These are used in linking steps and will be cleaned up afterward.
 }
 
-fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) -> ObjectModule {
+fn make_module(
+    sess: &Session,
+    backend_config: &BackendConfig,
+    name: String,
+) -> UnwindModule<ObjectModule> {
     let isa = crate::build_isa(sess, backend_config);
 
     let mut builder =
@@ -327,16 +332,15 @@ fn make_module(sess: &Session, backend_config: &BackendConfig, name: String) ->
     // is important, while cg_clif cares more about compilation times. Enabling -Zfunction-sections
     // can easily double the amount of time necessary to perform linking.
     builder.per_function_section(sess.opts.unstable_opts.function_sections.unwrap_or(false));
-    ObjectModule::new(builder)
+    UnwindModule::new(ObjectModule::new(builder), true)
 }
 
 fn emit_cgu(
     output_filenames: &OutputFilenames,
     prof: &SelfProfilerRef,
     name: String,
-    module: ObjectModule,
+    module: UnwindModule<ObjectModule>,
     debug: Option<DebugContext>,
-    unwind_context: UnwindContext,
     global_asm_object_file: Option<PathBuf>,
     producer: &str,
 ) -> Result<ModuleCodegenResult, String> {
@@ -346,8 +350,6 @@ fn emit_cgu(
         debug.emit(&mut product);
     }
 
-    unwind_context.emit(&mut product);
-
     let module_regular = emit_module(
         output_filenames,
         prof,
@@ -494,7 +496,6 @@ fn module_codegen(
 
             let mut cx = crate::CodegenCx::new(
                 tcx,
-                backend_config.clone(),
                 module.isa(),
                 tcx.sess.opts.debuginfo != DebugInfo::None,
                 cgu_name,
@@ -531,13 +532,7 @@ fn module_codegen(
                     }
                 }
             }
-            crate::main_shim::maybe_create_entry_wrapper(
-                tcx,
-                &mut module,
-                &mut cx.unwind_context,
-                false,
-                cgu.is_primary(),
-            );
+            crate::main_shim::maybe_create_entry_wrapper(tcx, &mut module, false, cgu.is_primary());
 
             let cgu_name = cgu.name().as_str().to_owned();
 
@@ -571,7 +566,6 @@ fn module_codegen(
                     cgu_name,
                     module,
                     cx.debug_context,
-                    cx.unwind_context,
                     global_asm_object_file,
                     &producer,
                 )
@@ -665,13 +659,10 @@ pub(crate) fn run_aot(
     });
 
     let mut allocator_module = make_module(tcx.sess, &backend_config, "allocator_shim".to_string());
-    let mut allocator_unwind_context = UnwindContext::new(allocator_module.isa(), true);
-    let created_alloc_shim =
-        crate::allocator::codegen(tcx, &mut allocator_module, &mut allocator_unwind_context);
+    let created_alloc_shim = crate::allocator::codegen(tcx, &mut allocator_module);
 
     let allocator_module = if created_alloc_shim {
-        let mut product = allocator_module.finish();
-        allocator_unwind_context.emit(&mut product);
+        let product = allocator_module.finish();
 
         match emit_module(
             tcx.output_filenames(()),
diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
index 4b149131b61..dfee8e714e6 100644
--- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs
+++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs
@@ -14,12 +14,12 @@ use rustc_session::Session;
 use rustc_span::Symbol;
 
 use crate::debuginfo::TypeDebugContext;
+use crate::unwind_module::UnwindModule;
 use crate::{prelude::*, BackendConfig};
 use crate::{CodegenCx, CodegenMode};
 
 struct JitState {
-    backend_config: BackendConfig,
-    jit_module: JITModule,
+    jit_module: UnwindModule<JITModule>,
 }
 
 thread_local! {
@@ -63,7 +63,7 @@ fn create_jit_module(
     tcx: TyCtxt<'_>,
     backend_config: &BackendConfig,
     hotswap: bool,
-) -> (JITModule, CodegenCx) {
+) -> (UnwindModule<JITModule>, CodegenCx) {
     let crate_info = CrateInfo::new(tcx, "dummy_target_cpu".to_string());
 
     let isa = crate::build_isa(tcx.sess, backend_config);
@@ -72,17 +72,11 @@ fn create_jit_module(
     crate::compiler_builtins::register_functions_for_jit(&mut jit_builder);
     jit_builder.symbol_lookup_fn(dep_symbol_lookup_fn(tcx.sess, crate_info));
     jit_builder.symbol("__clif_jit_fn", clif_jit_fn as *const u8);
-    let mut jit_module = JITModule::new(jit_builder);
+    let mut jit_module = UnwindModule::new(JITModule::new(jit_builder), false);
 
-    let mut cx = crate::CodegenCx::new(
-        tcx,
-        backend_config.clone(),
-        jit_module.isa(),
-        false,
-        Symbol::intern("dummy_cgu_name"),
-    );
+    let cx = crate::CodegenCx::new(tcx, jit_module.isa(), false, Symbol::intern("dummy_cgu_name"));
 
-    crate::allocator::codegen(tcx, &mut jit_module, &mut cx.unwind_context);
+    crate::allocator::codegen(tcx, &mut jit_module);
 
     (jit_module, cx)
 }
@@ -128,7 +122,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
                         );
                     }
                     CodegenMode::JitLazy => {
-                        codegen_shim(tcx, &mut cx, &mut cached_context, &mut jit_module, inst)
+                        codegen_shim(tcx, &mut cached_context, &mut jit_module, inst)
                     }
                 },
                 MonoItem::Static(def_id) => {
@@ -146,18 +140,11 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
         tcx.dcx().fatal("Inline asm is not supported in JIT mode");
     }
 
-    crate::main_shim::maybe_create_entry_wrapper(
-        tcx,
-        &mut jit_module,
-        &mut cx.unwind_context,
-        true,
-        true,
-    );
+    crate::main_shim::maybe_create_entry_wrapper(tcx, &mut jit_module, true, true);
 
     tcx.dcx().abort_if_errors();
 
-    jit_module.finalize_definitions().unwrap();
-    unsafe { cx.unwind_context.register_jit(&jit_module) };
+    jit_module.finalize_definitions();
 
     println!(
         "Rustc codegen cranelift will JIT run the executable, because -Cllvm-args=mode=jit was passed"
@@ -177,12 +164,12 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! {
         call_conv: jit_module.target_config().default_call_conv,
     };
     let start_func_id = jit_module.declare_function("main", Linkage::Import, &start_sig).unwrap();
-    let finalized_start: *const u8 = jit_module.get_finalized_function(start_func_id);
+    let finalized_start: *const u8 = jit_module.module.get_finalized_function(start_func_id);
 
     LAZY_JIT_STATE.with(|lazy_jit_state| {
         let mut lazy_jit_state = lazy_jit_state.borrow_mut();
         assert!(lazy_jit_state.is_none());
-        *lazy_jit_state = Some(JitState { backend_config, jit_module });
+        *lazy_jit_state = Some(JitState { jit_module });
     });
 
     let f: extern "C" fn(c_int, *const *const c_char) -> c_int =
@@ -268,7 +255,6 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
             let mut lazy_jit_state = lazy_jit_state.borrow_mut();
             let lazy_jit_state = lazy_jit_state.as_mut().unwrap();
             let jit_module = &mut lazy_jit_state.jit_module;
-            let backend_config = lazy_jit_state.backend_config.clone();
 
             let name = tcx.symbol_name(instance).name;
             let sig = crate::abi::get_function_sig(
@@ -278,7 +264,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
             );
             let func_id = jit_module.declare_function(name, Linkage::Export, &sig).unwrap();
 
-            let current_ptr = jit_module.read_got_entry(func_id);
+            let current_ptr = jit_module.module.read_got_entry(func_id);
 
             // If the function's GOT entry has already been updated to point at something other
             // than the shim trampoline, don't re-jit but just return the new pointer instead.
@@ -288,11 +274,10 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
                 return current_ptr;
             }
 
-            jit_module.prepare_for_function_redefine(func_id).unwrap();
+            jit_module.module.prepare_for_function_redefine(func_id).unwrap();
 
             let mut cx = crate::CodegenCx::new(
                 tcx,
-                backend_config,
                 jit_module.isa(),
                 false,
                 Symbol::intern("dummy_cgu_name"),
@@ -300,9 +285,8 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
             codegen_and_compile_fn(tcx, &mut cx, &mut Context::new(), jit_module, instance);
 
             assert!(cx.global_asm.is_empty());
-            jit_module.finalize_definitions().unwrap();
-            unsafe { cx.unwind_context.register_jit(&jit_module) };
-            jit_module.get_finalized_function(func_id)
+            jit_module.finalize_definitions();
+            jit_module.module.get_finalized_function(func_id)
         })
     })
 }
@@ -310,7 +294,7 @@ fn jit_fn(instance_ptr: *const Instance<'static>, trampoline_ptr: *const u8) ->
 fn dep_symbol_lookup_fn(
     sess: &Session,
     crate_info: CrateInfo,
-) -> Box<dyn Fn(&str) -> Option<*const u8>> {
+) -> Box<dyn Fn(&str) -> Option<*const u8> + Send> {
     use rustc_middle::middle::dependency_format::Linkage;
 
     let mut dylib_paths = Vec::new();
@@ -362,9 +346,8 @@ fn dep_symbol_lookup_fn(
 
 fn codegen_shim<'tcx>(
     tcx: TyCtxt<'tcx>,
-    cx: &mut CodegenCx,
     cached_context: &mut Context,
-    module: &mut JITModule,
+    module: &mut UnwindModule<JITModule>,
     inst: Instance<'tcx>,
 ) {
     let pointer_type = module.target_config().pointer_type();
@@ -413,5 +396,4 @@ fn codegen_shim<'tcx>(
     trampoline_builder.ins().return_(&ret_vals);
 
     module.define_function(func_id, context).unwrap();
-    cx.unwind_context.add_function(func_id, context, module.isa());
 }
diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
index c6b26dd873b..c88230c9360 100644
--- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs
+++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs
@@ -113,13 +113,7 @@ pub(crate) fn codegen_inline_asm_terminator<'tcx>(
                     );
                     let sig =
                         get_function_sig(fx.tcx, fx.target_config.default_call_conv, instance);
-                    create_wrapper_function(
-                        fx.module,
-                        &mut fx.cx.unwind_context,
-                        sig,
-                        &wrapper_name,
-                        symbol.name,
-                    );
+                    create_wrapper_function(fx.module, sig, &wrapper_name, symbol.name);
 
                     CInlineAsmOperand::Symbol { symbol: wrapper_name }
                 } else {
@@ -283,13 +277,7 @@ pub(crate) fn codegen_naked_asm<'tcx>(
                     );
                     let sig =
                         get_function_sig(tcx, module.target_config().default_call_conv, instance);
-                    create_wrapper_function(
-                        module,
-                        &mut cx.unwind_context,
-                        sig,
-                        &wrapper_name,
-                        symbol.name,
-                    );
+                    create_wrapper_function(module, sig, &wrapper_name, symbol.name);
 
                     CInlineAsmOperand::Symbol { symbol: wrapper_name }
                 } else {
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
index d454f3c1de7..399518e58d8 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/llvm_x86.rs
@@ -459,11 +459,20 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             intrinsic_args!(fx, args => (a); intrinsic);
             let a = a.load_scalar(fx);
 
+            let value = fx.bcx.ins().x86_cvtt2dq(types::I32X4, a);
+            let cvalue = CValue::by_val(value, ret.layout());
+            ret.write_cvalue(fx, cvalue);
+        }
+        "llvm.x86.sse2.cvtps2dq" => {
+            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_cvtps_epi32
+            intrinsic_args!(fx, args => (a); intrinsic);
+            let a = a.load_scalar(fx);
+
             // Using inline asm instead of fcvt_to_sint_sat as unrepresentable values are turned
             // into 0x80000000 for which Cranelift doesn't have a native instruction.
             codegen_inline_asm_inner(
                 fx,
-                &[InlineAsmTemplatePiece::String(format!("cvttps2dq xmm0, xmm0"))],
+                &[InlineAsmTemplatePiece::String(format!("cvtps2dq xmm0, xmm0"))],
                 &[CInlineAsmOperand::InOut {
                     reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::xmm0)),
                     _late: true,
@@ -1416,6 +1425,36 @@ pub(crate) fn codegen_x86_llvm_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, res);
         }
 
+        "llvm.x86.rdtsc" => {
+            // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_rdtsc&ig_expand=5273
+
+            let res_place = CPlace::new_stack_slot(
+                fx,
+                fx.layout_of(Ty::new_tup(fx.tcx, &[fx.tcx.types.u32, fx.tcx.types.u32])),
+            );
+            let eax_place = res_place.place_field(fx, FieldIdx::new(0));
+            let edx_place = res_place.place_field(fx, FieldIdx::new(1));
+            codegen_inline_asm_inner(
+                fx,
+                &[InlineAsmTemplatePiece::String("rdtsc".to_string())],
+                &[
+                    CInlineAsmOperand::Out {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::ax)),
+                        late: true,
+                        place: Some(eax_place),
+                    },
+                    CInlineAsmOperand::Out {
+                        reg: InlineAsmRegOrRegClass::Reg(InlineAsmReg::X86(X86InlineAsmReg::dx)),
+                        late: true,
+                        place: Some(edx_place),
+                    },
+                ],
+                InlineAsmOptions::NOSTACK | InlineAsmOptions::NOMEM,
+            );
+            let res = res_place.to_cvalue(fx);
+            ret.write_cvalue_transmute(fx, res);
+        }
+
         _ => {
             fx.tcx
                 .dcx()
diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs
index 06ca52b3903..192e6c91ea3 100644
--- a/compiler/rustc_codegen_cranelift/src/lib.rs
+++ b/compiler/rustc_codegen_cranelift/src/lib.rs
@@ -79,6 +79,7 @@ mod pretty_clif;
 mod toolchain;
 mod trap;
 mod unsize;
+mod unwind_module;
 mod value_and_place;
 mod vtable;
 
@@ -130,22 +131,13 @@ struct CodegenCx {
     global_asm: String,
     inline_asm_index: Cell<usize>,
     debug_context: Option<DebugContext>,
-    unwind_context: UnwindContext,
     cgu_name: Symbol,
 }
 
 impl CodegenCx {
-    fn new(
-        tcx: TyCtxt<'_>,
-        backend_config: BackendConfig,
-        isa: &dyn TargetIsa,
-        debug_info: bool,
-        cgu_name: Symbol,
-    ) -> Self {
+    fn new(tcx: TyCtxt<'_>, isa: &dyn TargetIsa, debug_info: bool, cgu_name: Symbol) -> Self {
         assert_eq!(pointer_ty(tcx), isa.pointer_type());
 
-        let unwind_context =
-            UnwindContext::new(isa, matches!(backend_config.codegen_mode, CodegenMode::Aot));
         let debug_context = if debug_info && !tcx.sess.target.options.is_like_windows {
             Some(DebugContext::new(tcx, isa, cgu_name.as_str()))
         } else {
@@ -158,7 +150,6 @@ impl CodegenCx {
             global_asm: String::new(),
             inline_asm_index: Cell::new(0),
             debug_context,
-            unwind_context,
             cgu_name,
         }
     }
@@ -175,7 +166,7 @@ impl CodegenBackend for CraneliftCodegenBackend {
     }
 
     fn init(&self, sess: &Session) {
-        use rustc_session::config::Lto;
+        use rustc_session::config::{InstrumentCoverage, Lto};
         match sess.lto() {
             Lto::No | Lto::ThinLocal => {}
             Lto::Thin | Lto::Fat => {
@@ -183,6 +174,11 @@ impl CodegenBackend for CraneliftCodegenBackend {
             }
         }
 
+        if sess.opts.cg.instrument_coverage() != InstrumentCoverage::No {
+            sess.dcx()
+                .fatal("`-Cinstrument-coverage` is LLVM specific and not supported by Cranelift");
+        }
+
         let mut config = self.config.borrow_mut();
         if config.is_none() {
             let new_config = BackendConfig::from_opts(&sess.opts.cg.llvm_args)
diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs
index f9a729618a5..fe0a1551419 100644
--- a/compiler/rustc_codegen_cranelift/src/main_shim.rs
+++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs
@@ -4,6 +4,7 @@ use rustc_middle::ty::AssocKind;
 use rustc_middle::ty::GenericArg;
 use rustc_session::config::{sigpipe, EntryFnType};
 use rustc_span::symbol::Ident;
+use rustc_span::DUMMY_SP;
 
 use crate::prelude::*;
 
@@ -11,8 +12,7 @@ use crate::prelude::*;
 /// users main function.
 pub(crate) fn maybe_create_entry_wrapper(
     tcx: TyCtxt<'_>,
-    module: &mut impl Module,
-    unwind_context: &mut UnwindContext,
+    module: &mut dyn Module,
     is_jit: bool,
     is_primary_cgu: bool,
 ) {
@@ -36,12 +36,11 @@ pub(crate) fn maybe_create_entry_wrapper(
         return;
     }
 
-    create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn, sigpipe);
+    create_entry_fn(tcx, module, main_def_id, is_jit, is_main_fn, sigpipe);
 
     fn create_entry_fn(
         tcx: TyCtxt<'_>,
-        m: &mut impl Module,
-        unwind_context: &mut UnwindContext,
+        m: &mut dyn Module,
         rust_main_def_id: DefId,
         ignore_lang_start_wrapper: bool,
         is_main_fn: bool,
@@ -121,6 +120,7 @@ pub(crate) fn maybe_create_entry_wrapper(
                     ParamEnv::reveal_all(),
                     report.def_id,
                     tcx.mk_args(&[GenericArg::from(main_ret_ty)]),
+                    DUMMY_SP,
                 )
                 .polymorphize(tcx);
 
@@ -146,6 +146,7 @@ pub(crate) fn maybe_create_entry_wrapper(
                     ParamEnv::reveal_all(),
                     start_def_id,
                     tcx.mk_args(&[main_ret_ty.into()]),
+                    DUMMY_SP,
                 )
                 .polymorphize(tcx);
                 let start_func_id = import_function(tcx, m, start_instance);
@@ -170,7 +171,5 @@ pub(crate) fn maybe_create_entry_wrapper(
         if let Err(err) = m.define_function(cmain_func_id, &mut ctx) {
             tcx.dcx().fatal(format!("entry symbol `{entry_name}` defined multiple times: {err}"));
         }
-
-        unwind_context.add_function(cmain_func_id, &ctx, m.isa());
     }
 }
diff --git a/compiler/rustc_codegen_cranelift/src/unwind_module.rs b/compiler/rustc_codegen_cranelift/src/unwind_module.rs
new file mode 100644
index 00000000000..b950aaa29ce
--- /dev/null
+++ b/compiler/rustc_codegen_cranelift/src/unwind_module.rs
@@ -0,0 +1,115 @@
+use cranelift_codegen::control::ControlPlane;
+use cranelift_codegen::ir::{Function, Signature};
+use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa};
+use cranelift_codegen::{Context, FinalizedMachReloc};
+use cranelift_module::{
+    DataDescription, DataId, FuncId, FuncOrDataId, Linkage, Module, ModuleDeclarations,
+    ModuleResult,
+};
+use cranelift_object::{ObjectModule, ObjectProduct};
+
+use crate::UnwindContext;
+
+/// A wrapper around a [Module] which adds any defined function to the [UnwindContext].
+pub(crate) struct UnwindModule<T> {
+    pub(crate) module: T,
+    unwind_context: UnwindContext,
+}
+
+impl<T: Module> UnwindModule<T> {
+    pub(crate) fn new(module: T, pic_eh_frame: bool) -> Self {
+        let unwind_context = UnwindContext::new(module.isa(), pic_eh_frame);
+        UnwindModule { module, unwind_context }
+    }
+}
+
+impl UnwindModule<ObjectModule> {
+    pub(crate) fn finish(self) -> ObjectProduct {
+        let mut product = self.module.finish();
+        self.unwind_context.emit(&mut product);
+        product
+    }
+}
+
+#[cfg(feature = "jit")]
+impl UnwindModule<cranelift_jit::JITModule> {
+    pub(crate) fn finalize_definitions(&mut self) {
+        self.module.finalize_definitions().unwrap();
+        let prev_unwind_context = std::mem::replace(
+            &mut self.unwind_context,
+            UnwindContext::new(self.module.isa(), false),
+        );
+        unsafe { prev_unwind_context.register_jit(&self.module) };
+    }
+}
+
+impl<T: Module> Module for UnwindModule<T> {
+    fn isa(&self) -> &dyn TargetIsa {
+        self.module.isa()
+    }
+
+    fn declarations(&self) -> &ModuleDeclarations {
+        self.module.declarations()
+    }
+
+    fn get_name(&self, name: &str) -> Option<FuncOrDataId> {
+        self.module.get_name(name)
+    }
+
+    fn target_config(&self) -> TargetFrontendConfig {
+        self.module.target_config()
+    }
+
+    fn declare_function(
+        &mut self,
+        name: &str,
+        linkage: Linkage,
+        signature: &Signature,
+    ) -> ModuleResult<FuncId> {
+        self.module.declare_function(name, linkage, signature)
+    }
+
+    fn declare_anonymous_function(&mut self, signature: &Signature) -> ModuleResult<FuncId> {
+        self.module.declare_anonymous_function(signature)
+    }
+
+    fn declare_data(
+        &mut self,
+        name: &str,
+        linkage: Linkage,
+        writable: bool,
+        tls: bool,
+    ) -> ModuleResult<DataId> {
+        self.module.declare_data(name, linkage, writable, tls)
+    }
+
+    fn declare_anonymous_data(&mut self, writable: bool, tls: bool) -> ModuleResult<DataId> {
+        self.module.declare_anonymous_data(writable, tls)
+    }
+
+    fn define_function_with_control_plane(
+        &mut self,
+        func: FuncId,
+        ctx: &mut Context,
+        ctrl_plane: &mut ControlPlane,
+    ) -> ModuleResult<()> {
+        self.module.define_function_with_control_plane(func, ctx, ctrl_plane)?;
+        self.unwind_context.add_function(func, ctx, self.module.isa());
+        Ok(())
+    }
+
+    fn define_function_bytes(
+        &mut self,
+        _func_id: FuncId,
+        _func: &Function,
+        _alignment: u64,
+        _bytes: &[u8],
+        _relocs: &[FinalizedMachReloc],
+    ) -> ModuleResult<()> {
+        unimplemented!()
+    }
+
+    fn define_data(&mut self, data_id: DataId, data: &DataDescription) -> ModuleResult<()> {
+        self.module.define_data(data_id, data)
+    }
+}
diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs
index 548c23cc794..fa8a1ec037c 100644
--- a/compiler/rustc_codegen_gcc/src/common.rs
+++ b/compiler/rustc_codegen_gcc/src/common.rs
@@ -28,6 +28,19 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
         global
         // TODO(antoyo): set linkage.
     }
+
+    pub fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
+        if value.get_type() == self.bool_type.make_pointer() {
+            if let Some(pointee) = typ.get_pointee() {
+                if pointee.dyncast_vector().is_some() {
+                    panic!()
+                }
+            }
+        }
+        // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
+        // SIMD builtins require a constant value.
+        self.bitcast_if_needed(value, typ)
+    }
 }
 
 pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> RValue<'gcc> {
@@ -207,7 +220,7 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
                         }
                         value
                     }
-                    GlobalAlloc::Function(fn_instance) => self.get_fn_addr(fn_instance),
+                    GlobalAlloc::Function { instance, .. } => self.get_fn_addr(instance),
                     GlobalAlloc::VTable(ty, trait_ref) => {
                         let alloc = self
                             .tcx
@@ -239,19 +252,6 @@ impl<'gcc, 'tcx> ConstMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         const_alloc_to_gcc(self, alloc)
     }
 
-    fn const_bitcast(&self, value: RValue<'gcc>, typ: Type<'gcc>) -> RValue<'gcc> {
-        if value.get_type() == self.bool_type.make_pointer() {
-            if let Some(pointee) = typ.get_pointee() {
-                if pointee.dyncast_vector().is_some() {
-                    panic!()
-                }
-            }
-        }
-        // NOTE: since bitcast makes a value non-constant, don't bitcast if not necessary as some
-        // SIMD builtins require a constant value.
-        self.bitcast_if_needed(value, typ)
-    }
-
     fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
         self.context
             .new_array_access(None, base_addr, self.const_usize(offset.bytes()))
diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs
index 4a1f5188a80..1d689c9ac0e 100644
--- a/compiler/rustc_codegen_gcc/src/context.rs
+++ b/compiler/rustc_codegen_gcc/src/context.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::layout::{
 };
 use rustc_middle::ty::{self, Instance, ParamEnv, PolyExistentialTraitRef, Ty, TyCtxt};
 use rustc_session::Session;
-use rustc_span::{source_map::respan, Span};
+use rustc_span::{source_map::respan, Span, DUMMY_SP};
 use rustc_target::abi::{
     call::FnAbi, HasDataLayout, PointeeInfo, Size, TargetDataLayout, VariantIdx,
 };
@@ -27,7 +27,6 @@ use crate::callee::get_fn;
 use crate::common::SignType;
 
 pub struct CodegenCx<'gcc, 'tcx> {
-    pub check_overflow: bool,
     pub codegen_unit: &'tcx CodegenUnit<'tcx>,
     pub context: &'gcc Context<'gcc>,
 
@@ -134,8 +133,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
         tcx: TyCtxt<'tcx>,
         supports_128bit_integers: bool,
     ) -> Self {
-        let check_overflow = tcx.sess.overflow_checks();
-
         let create_type = |ctype, rust_type| {
             let layout = tcx.layout_of(ParamEnv::reveal_all().and(rust_type)).unwrap();
             let align = layout.align.abi.bytes();
@@ -271,7 +268,6 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
         }
 
         let mut cx = Self {
-            check_overflow,
             codegen_unit,
             context,
             current_func: RefCell::new(None),
@@ -483,6 +479,7 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
                     ty::ParamEnv::reveal_all(),
                     def_id,
                     ty::List::empty(),
+                    DUMMY_SP,
                 );
 
                 let symbol_name = tcx.symbol_name(instance).name;
@@ -511,10 +508,6 @@ impl<'gcc, 'tcx> MiscMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         &self.tcx.sess
     }
 
-    fn check_overflow(&self) -> bool {
-        self.check_overflow
-    }
-
     fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> {
         self.codegen_unit
     }
diff --git a/compiler/rustc_codegen_gcc/src/type_.rs b/compiler/rustc_codegen_gcc/src/type_.rs
index 68471b028be..4caff2e6310 100644
--- a/compiler/rustc_codegen_gcc/src/type_.rs
+++ b/compiler/rustc_codegen_gcc/src/type_.rs
@@ -89,13 +89,34 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
             ty::FloatTy::F128 => self.type_f128(),
         }
     }
-}
 
-impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
-    fn type_i1(&self) -> Type<'gcc> {
+    pub fn type_i1(&self) -> Type<'gcc> {
         self.bool_type
     }
 
+    pub fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> {
+        let types = fields.to_vec();
+        if let Some(typ) = self.struct_types.borrow().get(fields) {
+            return *typ;
+        }
+        let fields: Vec<_> = fields
+            .iter()
+            .enumerate()
+            .map(|(index, field)| {
+                self.context.new_field(None, *field, format!("field{}_TODO", index))
+            })
+            .collect();
+        let typ = self.context.new_struct_type(None, "struct", &fields).as_type();
+        if packed {
+            #[cfg(feature = "master")]
+            typ.set_packed();
+        }
+        self.struct_types.borrow_mut().insert(types, typ);
+        typ
+    }
+}
+
+impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
     fn type_i8(&self) -> Type<'gcc> {
         self.i8_type
     }
@@ -131,7 +152,7 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
     fn type_f64(&self) -> Type<'gcc> {
         self.double_type
     }
-    
+
     fn type_f128(&self) -> Type<'gcc> {
         unimplemented!("f16_f128")
     }
@@ -140,27 +161,6 @@ impl<'gcc, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'gcc, 'tcx> {
         self.context.new_function_pointer_type(None, return_type, params, false)
     }
 
-    fn type_struct(&self, fields: &[Type<'gcc>], packed: bool) -> Type<'gcc> {
-        let types = fields.to_vec();
-        if let Some(typ) = self.struct_types.borrow().get(fields) {
-            return *typ;
-        }
-        let fields: Vec<_> = fields
-            .iter()
-            .enumerate()
-            .map(|(index, field)| {
-                self.context.new_field(None, *field, format!("field{}_TODO", index))
-            })
-            .collect();
-        let typ = self.context.new_struct_type(None, "struct", &fields).as_type();
-        if packed {
-            #[cfg(feature = "master")]
-            typ.set_packed();
-        }
-        self.struct_types.borrow_mut().insert(types, typ);
-        typ
-    }
-
     fn type_kind(&self, typ: Type<'gcc>) -> TypeKind {
         if self.is_int_type_or_bool(typ) {
             TypeKind::Integer
diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs
index a6a3f0f9646..d034f9b5256 100644
--- a/compiler/rustc_codegen_llvm/src/abi.rs
+++ b/compiler/rustc_codegen_llvm/src/abi.rs
@@ -226,7 +226,8 @@ impl<'ll, 'tcx> ArgAbiExt<'ll, 'tcx> for ArgAbi<'tcx, Ty<'tcx>> {
                 //   when passed by value, making it smaller.
                 // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
                 //   when passed by value, making it larger.
-                let copy_bytes = cmp::min(scratch_size.bytes(), self.layout.size.bytes());
+                let copy_bytes =
+                    cmp::min(cast.unaligned_size(bx).bytes(), self.layout.size.bytes());
                 // Allocate some scratch space...
                 let llscratch = bx.alloca(scratch_size, scratch_align);
                 bx.lifetime_start(llscratch, scratch_size);
diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs
index 48693895da1..cd82894af18 100644
--- a/compiler/rustc_codegen_llvm/src/attributes.rs
+++ b/compiler/rustc_codegen_llvm/src/attributes.rs
@@ -2,7 +2,7 @@
 
 use rustc_codegen_ssa::traits::*;
 use rustc_hir::def_id::DefId;
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, PatchableFunctionEntry};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_session::config::{FunctionReturn, OptLevel};
 use rustc_span::symbol::sym;
@@ -53,6 +53,34 @@ fn inline_attr<'ll>(cx: &CodegenCx<'ll, '_>, inline: InlineAttr) -> Option<&'ll
     }
 }
 
+#[inline]
+fn patchable_function_entry_attrs<'ll>(
+    cx: &CodegenCx<'ll, '_>,
+    attr: Option<PatchableFunctionEntry>,
+) -> SmallVec<[&'ll Attribute; 2]> {
+    let mut attrs = SmallVec::new();
+    let patchable_spec = attr.unwrap_or_else(|| {
+        PatchableFunctionEntry::from_config(cx.tcx.sess.opts.unstable_opts.patchable_function_entry)
+    });
+    let entry = patchable_spec.entry();
+    let prefix = patchable_spec.prefix();
+    if entry > 0 {
+        attrs.push(llvm::CreateAttrStringValue(
+            cx.llcx,
+            "patchable-function-entry",
+            &format!("{}", entry),
+        ));
+    }
+    if prefix > 0 {
+        attrs.push(llvm::CreateAttrStringValue(
+            cx.llcx,
+            "patchable-function-prefix",
+            &format!("{}", prefix),
+        ));
+    }
+    attrs
+}
+
 /// Get LLVM sanitize attributes.
 #[inline]
 pub fn sanitize_attrs<'ll>(
@@ -421,6 +449,7 @@ pub fn from_fn_attrs<'ll, 'tcx>(
         llvm::set_alignment(llfn, align);
     }
     to_add.extend(sanitize_attrs(cx, codegen_fn_attrs.no_sanitize));
+    to_add.extend(patchable_function_entry_attrs(cx, codegen_fn_attrs.patchable_function_entry));
 
     // Always annotate functions with the target-cpu they are compiled for.
     // Without this, ThinLTO won't inline Rust functions into Clang generated
diff --git a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
index 28a88dd2efe..b72636a6224 100644
--- a/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
+++ b/compiler/rustc_codegen_llvm/src/back/owned_target_machine.rs
@@ -32,7 +32,7 @@ impl OwnedTargetMachine {
         unique_section_names: bool,
         trap_unreachable: bool,
         singletree: bool,
-        asm_comments: bool,
+        verbose_asm: bool,
         emit_stack_size_section: bool,
         relax_elf_relocations: bool,
         use_init_array: bool,
@@ -64,7 +64,7 @@ impl OwnedTargetMachine {
                 unique_section_names,
                 trap_unreachable,
                 singletree,
-                asm_comments,
+                verbose_asm,
                 emit_stack_size_section,
                 relax_elf_relocations,
                 use_init_array,
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 5e481eb98f5..2fda19bf0c9 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -214,7 +214,7 @@ pub fn target_machine_factory(
         sess.opts.unstable_opts.trap_unreachable.unwrap_or(sess.target.trap_unreachable);
     let emit_stack_size_section = sess.opts.unstable_opts.emit_stack_sizes;
 
-    let asm_comments = sess.opts.unstable_opts.asm_comments;
+    let verbose_asm = sess.opts.unstable_opts.verbose_asm;
     let relax_elf_relocations =
         sess.opts.unstable_opts.relax_elf_relocations.unwrap_or(sess.target.relax_elf_relocations);
 
@@ -289,7 +289,7 @@ pub fn target_machine_factory(
             funique_section_names,
             trap_unreachable,
             singlethread,
-            asm_comments,
+            verbose_asm,
             emit_stack_size_section,
             relax_elf_relocations,
             use_init_array,
diff --git a/compiler/rustc_codegen_llvm/src/common.rs b/compiler/rustc_codegen_llvm/src/common.rs
index 4ffc92eb633..fe64649cf70 100644
--- a/compiler/rustc_codegen_llvm/src/common.rs
+++ b/compiler/rustc_codegen_llvm/src/common.rs
@@ -289,8 +289,8 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                             (value, AddressSpace::DATA)
                         }
                     }
-                    GlobalAlloc::Function(fn_instance) => (
-                        self.get_fn_addr(fn_instance.polymorphize(self.tcx)),
+                    GlobalAlloc::Function { instance, .. } => (
+                        self.get_fn_addr(instance.polymorphize(self.tcx)),
                         self.data_layout().instruction_address_space,
                     ),
                     GlobalAlloc::VTable(ty, trait_ref) => {
@@ -329,10 +329,6 @@ impl<'ll, 'tcx> ConstMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         const_alloc_to_llvm(self, alloc, /*static*/ false)
     }
 
-    fn const_bitcast(&self, val: &'ll Value, ty: &'ll Type) -> &'ll Value {
-        self.const_bitcast(val, ty)
-    }
-
     fn const_ptr_byte_offset(&self, base_addr: Self::Value, offset: abi::Size) -> Self::Value {
         unsafe {
             llvm::LLVMConstInBoundsGEP2(
diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs
index 7d92888feee..77beb9a6bb3 100644
--- a/compiler/rustc_codegen_llvm/src/context.rs
+++ b/compiler/rustc_codegen_llvm/src/context.rs
@@ -3,6 +3,7 @@ use crate::back::write::to_llvm_code_model;
 use crate::callee::get_fn;
 use crate::coverageinfo;
 use crate::debuginfo;
+use crate::debuginfo::metadata::apply_vcall_visibility_metadata;
 use crate::llvm;
 use crate::llvm_util;
 use crate::type_::Type;
@@ -27,7 +28,7 @@ use rustc_session::config::{BranchProtection, CFGuard, CFProtection};
 use rustc_session::config::{CrateType, DebugInfo, PAuthKey, PacRet};
 use rustc_session::Session;
 use rustc_span::source_map::Spanned;
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::{call::FnAbi, HasDataLayout, TargetDataLayout, VariantIdx};
 use rustc_target::spec::{HasTargetSpec, RelocModel, Target, TlsModel};
 use smallvec::SmallVec;
@@ -43,7 +44,6 @@ use std::str;
 /// All other LLVM data structures in the `CodegenCx` are tied to that `llvm::Context`.
 pub struct CodegenCx<'ll, 'tcx> {
     pub tcx: TyCtxt<'tcx>,
-    pub check_overflow: bool,
     pub use_dll_storage_attrs: bool,
     pub tls_model: llvm::ThreadLocalMode,
 
@@ -441,8 +441,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
         // start) and then strongly recommending static linkage on Windows!
         let use_dll_storage_attrs = tcx.sess.target.is_like_windows;
 
-        let check_overflow = tcx.sess.overflow_checks();
-
         let tls_model = to_llvm_tls_model(tcx.sess.tls_model());
 
         let (llcx, llmod) = (&*llvm_module.llcx, llvm_module.llmod());
@@ -466,7 +464,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
 
         CodegenCx {
             tcx,
-            check_overflow,
             use_dll_storage_attrs,
             tls_model,
             llmod,
@@ -522,6 +519,15 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         &self.vtables
     }
 
+    fn apply_vcall_visibility_metadata(
+        &self,
+        ty: Ty<'tcx>,
+        poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
+        vtable: &'ll Value,
+    ) {
+        apply_vcall_visibility_metadata(self, ty, poly_trait_ref, vtable);
+    }
+
     fn get_fn(&self, instance: Instance<'tcx>) -> &'ll Value {
         get_fn(self, instance)
     }
@@ -574,6 +580,7 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
                 ty::ParamEnv::reveal_all(),
                 def_id,
                 ty::List::empty(),
+                DUMMY_SP,
             )),
             _ => {
                 let name = name.unwrap_or("rust_eh_personality");
@@ -596,10 +603,6 @@ impl<'ll, 'tcx> MiscMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         self.tcx.sess
     }
 
-    fn check_overflow(&self) -> bool {
-        self.check_overflow
-    }
-
     fn codegen_unit(&self) -> &'tcx CodegenUnit<'tcx> {
         self.codegen_unit
     }
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 8de4e0effad..851a4c42e99 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -705,10 +705,12 @@ impl MsvcBasicName for ty::UintTy {
 
 impl MsvcBasicName for ty::FloatTy {
     fn msvc_basic_name(self) -> &'static str {
-        // FIXME: f16 and f128 have no MSVC representation. We could improve the debuginfo.
-        // See: <https://github.com/rust-lang/rust/pull/114607/files#r1454683264>
+        // FIXME(f16_f128): `f16` and `f128` have no MSVC representation. We could improve the
+        // debuginfo. See: <https://github.com/rust-lang/rust/issues/121837>
         match self {
-            ty::FloatTy::F16 => "half",
+            ty::FloatTy::F16 => {
+                bug!("`f16` should have been handled in `build_basic_type_di_node`")
+            }
             ty::FloatTy::F32 => "float",
             ty::FloatTy::F64 => "double",
             ty::FloatTy::F128 => "fp128",
@@ -716,6 +718,38 @@ impl MsvcBasicName for ty::FloatTy {
     }
 }
 
+fn build_cpp_f16_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) -> DINodeCreationResult<'ll> {
+    // MSVC has no native support for `f16`. Instead, emit `struct f16 { bits: u16 }` to allow the
+    // `f16`'s value to be displayed using a Natvis visualiser in `intrinsic.natvis`.
+    let float_ty = cx.tcx.types.f16;
+    let bits_ty = cx.tcx.types.u16;
+    type_map::build_type_with_children(
+        cx,
+        type_map::stub(
+            cx,
+            Stub::Struct,
+            UniqueTypeId::for_ty(cx.tcx, float_ty),
+            "f16",
+            cx.size_and_align_of(float_ty),
+            NO_SCOPE_METADATA,
+            DIFlags::FlagZero,
+        ),
+        // Fields:
+        |cx, float_di_node| {
+            smallvec![build_field_di_node(
+                cx,
+                float_di_node,
+                "bits",
+                cx.size_and_align_of(bits_ty),
+                Size::ZERO,
+                DIFlags::FlagZero,
+                type_di_node(cx, bits_ty),
+            )]
+        },
+        NO_GENERICS,
+    )
+}
+
 fn build_basic_type_di_node<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     t: Ty<'tcx>,
@@ -739,6 +773,9 @@ fn build_basic_type_di_node<'ll, 'tcx>(
         ty::Char => ("char", DW_ATE_UTF),
         ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed),
         ty::Uint(uint_ty) if cpp_like_debuginfo => (uint_ty.msvc_basic_name(), DW_ATE_unsigned),
+        ty::Float(ty::FloatTy::F16) if cpp_like_debuginfo => {
+            return build_cpp_f16_di_node(cx);
+        }
         ty::Float(float_ty) if cpp_like_debuginfo => (float_ty.msvc_basic_name(), DW_ATE_float),
         ty::Int(int_ty) => (int_ty.name_str(), DW_ATE_signed),
         ty::Uint(uint_ty) => (uint_ty.name_str(), DW_ATE_unsigned),
@@ -1449,12 +1486,18 @@ fn build_vtable_type_di_node<'ll, 'tcx>(
     .di_node
 }
 
-fn vcall_visibility_metadata<'ll, 'tcx>(
+pub(crate) fn apply_vcall_visibility_metadata<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     ty: Ty<'tcx>,
     trait_ref: Option<PolyExistentialTraitRef<'tcx>>,
     vtable: &'ll Value,
 ) {
+    // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
+    // LLVM at the moment.
+    if !cx.sess().opts.unstable_opts.virtual_function_elimination || cx.sess().lto() != Lto::Fat {
+        return;
+    }
+
     enum VCallVisibility {
         Public = 0,
         LinkageUnit = 1,
@@ -1531,12 +1574,6 @@ pub fn create_vtable_di_node<'ll, 'tcx>(
     poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
     vtable: &'ll Value,
 ) {
-    // FIXME(flip1995): The virtual function elimination optimization only works with full LTO in
-    // LLVM at the moment.
-    if cx.sess().opts.unstable_opts.virtual_function_elimination && cx.sess().lto() == Lto::Fat {
-        vcall_visibility_metadata(cx, ty, poly_trait_ref, vtable);
-    }
-
     if cx.dbg_cx.is_none() {
         return;
     }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index b5b0086f740..e02c61cd296 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1121,8 +1121,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     if name == sym::simd_select_bitmask {
         let (len, _) = require_simd!(arg_tys[1], SimdArgument);
 
-        let expected_int_bits = (len.max(8) - 1).next_power_of_two();
-        let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
+        let expected_int_bits = len.max(8).next_power_of_two();
+        let expected_bytes = len.div_ceil(8);
 
         let mask_ty = arg_tys[0];
         let mask = match mask_ty.kind() {
@@ -1379,17 +1379,16 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     }
 
     if name == sym::simd_bitmask {
-        // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a
-        // vector mask and returns the most significant bit (MSB) of each lane in the form
-        // of either:
+        // The `fn simd_bitmask(vector) -> unsigned integer` intrinsic takes a vector mask and
+        // returns one bit for each lane (which must all be `0` or `!0`) in the form of either:
         // * an unsigned integer
         // * an array of `u8`
         // If the vector has less than 8 lanes, a u8 is returned with zeroed trailing bits.
         //
         // The bit order of the result depends on the byte endianness, LSB-first for little
         // endian and MSB-first for big endian.
-        let expected_int_bits = in_len.max(8);
-        let expected_bytes = expected_int_bits / 8 + ((expected_int_bits % 8 > 0) as u64);
+        let expected_int_bits = in_len.max(8).next_power_of_two();
+        let expected_bytes = in_len.div_ceil(8);
 
         // Integer vector <i{in_bitwidth} x in_len>:
         let (i_xn, in_elem_bitwidth) = match in_elem.kind() {
@@ -1409,7 +1408,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
             }),
         };
 
-        // Shift the MSB to the right by "in_elem_bitwidth - 1" into the first bit position.
+        // LLVM doesn't always know the inputs are `0` or `!0`, so we shift here so it optimizes to
+        // `pmovmskb` and similar on x86.
         let shift_indices =
             vec![
                 bx.cx.const_int(bx.type_ix(in_elem_bitwidth), (in_elem_bitwidth - 1) as _);
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index 4b7a2643007..ed0989a0ba4 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -274,10 +274,11 @@ impl CodegenBackend for LlvmCodegenBackend {
             |tcx, ()| llvm_util::global_llvm_features(tcx.sess, true)
     }
 
-    fn print(&self, req: &PrintRequest, out: &mut dyn PrintBackendInfo, sess: &Session) {
+    fn print(&self, req: &PrintRequest, out: &mut String, sess: &Session) {
+        use std::fmt::Write;
         match req.kind {
             PrintKind::RelocationModels => {
-                writeln!(out, "Available relocation models:");
+                writeln!(out, "Available relocation models:").unwrap();
                 for name in &[
                     "static",
                     "pic",
@@ -288,25 +289,25 @@ impl CodegenBackend for LlvmCodegenBackend {
                     "ropi-rwpi",
                     "default",
                 ] {
-                    writeln!(out, "    {name}");
+                    writeln!(out, "    {name}").unwrap();
                 }
-                writeln!(out);
+                writeln!(out).unwrap();
             }
             PrintKind::CodeModels => {
-                writeln!(out, "Available code models:");
+                writeln!(out, "Available code models:").unwrap();
                 for name in &["tiny", "small", "kernel", "medium", "large"] {
-                    writeln!(out, "    {name}");
+                    writeln!(out, "    {name}").unwrap();
                 }
-                writeln!(out);
+                writeln!(out).unwrap();
             }
             PrintKind::TlsModels => {
-                writeln!(out, "Available TLS models:");
+                writeln!(out, "Available TLS models:").unwrap();
                 for name in
                     &["global-dynamic", "local-dynamic", "initial-exec", "local-exec", "emulated"]
                 {
-                    writeln!(out, "    {name}");
+                    writeln!(out, "    {name}").unwrap();
                 }
-                writeln!(out);
+                writeln!(out).unwrap();
             }
             PrintKind::StackProtectorStrategies => {
                 writeln!(
@@ -332,7 +333,8 @@ impl CodegenBackend for LlvmCodegenBackend {
     none
         Do not generate stack canaries.
 "#
-                );
+                )
+                .unwrap();
             }
             _other => llvm_util::print(req, out, sess),
         }
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index 132e1f9e8fd..08e9e312827 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -2185,7 +2185,7 @@ extern "C" {
         UniqueSectionNames: bool,
         TrapUnreachable: bool,
         Singlethread: bool,
-        AsmComments: bool,
+        VerboseAsm: bool,
         EmitStackSizeSection: bool,
         RelaxELFRelocations: bool,
         UseInitArray: bool,
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index 7e0f264a4ae..0e89e66be49 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -6,7 +6,6 @@ use crate::errors::{
 use crate::llvm;
 use libc::c_int;
 use rustc_codegen_ssa::base::wants_wasm_eh;
-use rustc_codegen_ssa::traits::PrintBackendInfo;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::small_c_str::SmallCStr;
 use rustc_fs_util::path_to_c_string;
@@ -18,6 +17,7 @@ use rustc_target::spec::{MergeFunctions, PanicStrategy};
 use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
 
 use std::ffi::{c_char, c_void, CStr, CString};
+use std::fmt::Write;
 use std::path::Path;
 use std::ptr;
 use std::slice;
@@ -372,7 +372,7 @@ fn llvm_target_features(tm: &llvm::TargetMachine) -> Vec<(&str, &str)> {
     ret
 }
 
-fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &llvm::TargetMachine) {
+fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMachine) {
     let mut llvm_target_features = llvm_target_features(tm);
     let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
     let mut rustc_target_features = sess
@@ -412,24 +412,26 @@ fn print_target_features(out: &mut dyn PrintBackendInfo, sess: &Session, tm: &ll
         .max()
         .unwrap_or(0);
 
-    writeln!(out, "Features supported by rustc for this target:");
+    writeln!(out, "Features supported by rustc for this target:").unwrap();
     for (feature, desc) in &rustc_target_features {
-        writeln!(out, "    {feature:max_feature_len$} - {desc}.");
+        writeln!(out, "    {feature:max_feature_len$} - {desc}.").unwrap();
     }
-    writeln!(out, "\nCode-generation features supported by LLVM for this target:");
+    writeln!(out, "\nCode-generation features supported by LLVM for this target:").unwrap();
     for (feature, desc) in &llvm_target_features {
-        writeln!(out, "    {feature:max_feature_len$} - {desc}.");
+        writeln!(out, "    {feature:max_feature_len$} - {desc}.").unwrap();
     }
     if llvm_target_features.is_empty() {
-        writeln!(out, "    Target features listing is not supported by this LLVM version.");
+        writeln!(out, "    Target features listing is not supported by this LLVM version.")
+            .unwrap();
     }
-    writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it.");
-    writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n");
-    writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],");
-    writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n");
+    writeln!(out, "\nUse +feature to enable a feature, or -feature to disable it.").unwrap();
+    writeln!(out, "For example, rustc -C target-cpu=mycpu -C target-feature=+feature1,-feature2\n")
+        .unwrap();
+    writeln!(out, "Code-generation features cannot be used in cfg or #[target_feature],").unwrap();
+    writeln!(out, "and may be renamed or removed in a future version of LLVM or rustc.\n").unwrap();
 }
 
-pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess: &Session) {
+pub(crate) fn print(req: &PrintRequest, mut out: &mut String, sess: &Session) {
     require_inited();
     let tm = create_informational_target_machine(sess);
     match req.kind {
@@ -440,9 +442,9 @@ pub(crate) fn print(req: &PrintRequest, mut out: &mut dyn PrintBackendInfo, sess
             let cpu_cstring = CString::new(handle_native(sess.target.cpu.as_ref()))
                 .unwrap_or_else(|e| bug!("failed to convert to cstring: {}", e));
             unsafe extern "C" fn callback(out: *mut c_void, string: *const c_char, len: usize) {
-                let out = &mut *(out as *mut &mut dyn PrintBackendInfo);
+                let out = &mut *(out as *mut &mut String);
                 let bytes = slice::from_raw_parts(string as *const u8, len);
-                write!(out, "{}", String::from_utf8_lossy(bytes));
+                write!(out, "{}", String::from_utf8_lossy(bytes)).unwrap();
             }
             unsafe {
                 llvm::LLVMRustPrintTargetCPUs(
diff --git a/compiler/rustc_codegen_llvm/src/type_.rs b/compiler/rustc_codegen_llvm/src/type_.rs
index a00f09dc40d..f1141c57ced 100644
--- a/compiler/rustc_codegen_llvm/src/type_.rs
+++ b/compiler/rustc_codegen_llvm/src/type_.rs
@@ -127,13 +127,24 @@ impl<'ll> CodegenCx<'ll, '_> {
     pub(crate) fn type_variadic_func(&self, args: &[&'ll Type], ret: &'ll Type) -> &'ll Type {
         unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, True) }
     }
-}
 
-impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
-    fn type_i1(&self) -> &'ll Type {
+    pub(crate) fn type_i1(&self) -> &'ll Type {
         unsafe { llvm::LLVMInt1TypeInContext(self.llcx) }
     }
 
+    pub(crate) fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type {
+        unsafe {
+            llvm::LLVMStructTypeInContext(
+                self.llcx,
+                els.as_ptr(),
+                els.len() as c_uint,
+                packed as Bool,
+            )
+        }
+    }
+}
+
+impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
     fn type_i8(&self) -> &'ll Type {
         unsafe { llvm::LLVMInt8TypeInContext(self.llcx) }
     }
@@ -178,17 +189,6 @@ impl<'ll, 'tcx> BaseTypeMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         unsafe { llvm::LLVMFunctionType(ret, args.as_ptr(), args.len() as c_uint, False) }
     }
 
-    fn type_struct(&self, els: &[&'ll Type], packed: bool) -> &'ll Type {
-        unsafe {
-            llvm::LLVMStructTypeInContext(
-                self.llcx,
-                els.as_ptr(),
-                els.len() as c_uint,
-                packed as Bool,
-            )
-        }
-    }
-
     fn type_kind(&self, ty: &'ll Type) -> TypeKind {
         unsafe { llvm::LLVMRustGetTypeKind(ty).to_generic() }
     }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index d509e4ce56d..1f627353d54 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -45,7 +45,7 @@ use tempfile::Builder as TempFileBuilder;
 
 use itertools::Itertools;
 use std::collections::BTreeSet;
-use std::ffi::{OsStr, OsString};
+use std::ffi::OsString;
 use std::fs::{read, File, OpenOptions};
 use std::io::{BufWriter, Write};
 use std::ops::Deref;
@@ -1306,12 +1306,12 @@ fn link_sanitizer_runtime(
         let filename = format!("rustc{channel}_rt.{name}");
         let path = find_sanitizer_runtime(sess, &filename);
         let rpath = path.to_str().expect("non-utf8 component in path");
-        linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
+        linker.cc_args(&["-Wl,-rpath", "-Xlinker", rpath]);
         linker.link_dylib_by_name(&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");
+        linker.link_arg("/INFERASANLIBS");
     } else {
         let filename = format!("librustc{channel}_rt.{name}.a");
         let path = find_sanitizer_runtime(sess, &filename).join(&filename);
@@ -1888,9 +1888,9 @@ fn add_post_link_objects(
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_pre_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     if let Some(args) = sess.target.pre_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
-    cmd.args(&sess.opts.unstable_opts.pre_link_args);
+    cmd.verbatim_args(&sess.opts.unstable_opts.pre_link_args);
 }
 
 /// Add a link script embedded in the target, if applicable.
@@ -1908,8 +1908,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
                 sess.dcx().emit_fatal(errors::LinkScriptWriteFailure { path, error });
             }
 
-            cmd.arg("--script");
-            cmd.arg(path);
+            cmd.link_arg("--script").link_arg(path);
         }
         _ => {}
     }
@@ -1918,7 +1917,7 @@ fn add_link_script(cmd: &mut dyn Linker, sess: &Session, tmpdir: &Path, crate_ty
 /// Add arbitrary "user defined" args defined from command line.
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_user_defined_link_args(cmd: &mut dyn Linker, sess: &Session) {
-    cmd.args(&sess.opts.cg.link_args);
+    cmd.verbatim_args(&sess.opts.cg.link_args);
 }
 
 /// Add arbitrary "late link" args defined by the target spec.
@@ -1936,15 +1935,15 @@ fn add_late_link_args(
         });
     if any_dynamic_crate {
         if let Some(args) = sess.target.late_link_args_dynamic.get(&flavor) {
-            cmd.args(args.iter().map(Deref::deref));
+            cmd.verbatim_args(args.iter().map(Deref::deref));
         }
     } else {
         if let Some(args) = sess.target.late_link_args_static.get(&flavor) {
-            cmd.args(args.iter().map(Deref::deref));
+            cmd.verbatim_args(args.iter().map(Deref::deref));
         }
     }
     if let Some(args) = sess.target.late_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
 }
 
@@ -1952,7 +1951,7 @@ fn add_late_link_args(
 /// FIXME: Determine where exactly these args need to be inserted.
 fn add_post_link_args(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
     if let Some(args) = sess.target.post_link_args.get(&flavor) {
-        cmd.args(args.iter().map(Deref::deref));
+        cmd.verbatim_args(args.iter().map(Deref::deref));
     }
 }
 
@@ -2097,6 +2096,10 @@ fn add_rpath_args(
     codegen_results: &CodegenResults,
     out_filename: &Path,
 ) {
+    if !sess.target.has_rpath {
+        return;
+    }
+
     // FIXME (#2397): At some point we want to rpath our guesses as to
     // where extern libraries might live, based on the
     // add_lib_search_paths
@@ -2115,11 +2118,10 @@ fn add_rpath_args(
         let rpath_config = RPathConfig {
             libs: &*libs,
             out_filename: out_filename.to_path_buf(),
-            has_rpath: sess.target.has_rpath,
             is_like_osx: sess.target.is_like_osx,
             linker_is_gnu: sess.target.linker_flavor.is_gnu(),
         };
-        cmd.args(&rpath::get_rpath_flags(&rpath_config));
+        cmd.cc_args(&rpath::get_rpath_flags(&rpath_config));
     }
 }
 
@@ -2378,7 +2380,7 @@ fn add_order_independent_options(
         } else {
             ""
         };
-        cmd.arg(format!("--dynamic-linker={prefix}ld.so.1"));
+        cmd.link_arg(format!("--dynamic-linker={prefix}ld.so.1"));
     }
 
     if sess.target.eh_frame_header {
@@ -2393,8 +2395,7 @@ fn add_order_independent_options(
     }
 
     if sess.target.os == "emscripten" {
-        cmd.arg("-s");
-        cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort {
+        cmd.cc_arg("-s").cc_arg(if sess.panic_strategy() == PanicStrategy::Abort {
             "DISABLE_EXCEPTION_CATCHING=1"
         } else {
             "DISABLE_EXCEPTION_CATCHING=0"
@@ -2402,22 +2403,21 @@ fn add_order_independent_options(
     }
 
     if flavor == LinkerFlavor::Llbc {
-        cmd.arg("--target");
-        cmd.arg(sess.target.llvm_target.as_ref());
-        cmd.arg("--target-cpu");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&[
+            "--target",
+            sess.target.llvm_target.as_ref(),
+            "--target-cpu",
+            &codegen_results.crate_info.target_cpu,
+        ]);
     } else if flavor == LinkerFlavor::Ptx {
-        cmd.arg("--fallback-arch");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&["--fallback-arch", &codegen_results.crate_info.target_cpu]);
     } else if flavor == LinkerFlavor::Bpf {
-        cmd.arg("--cpu");
-        cmd.arg(&codegen_results.crate_info.target_cpu);
+        cmd.link_args(&["--cpu", &codegen_results.crate_info.target_cpu]);
         if let Some(feat) = [sess.opts.cg.target_feature.as_str(), &sess.target.options.features]
             .into_iter()
             .find(|feat| !feat.is_empty())
         {
-            cmd.arg("--cpu-features");
-            cmd.arg(feat);
+            cmd.link_args(&["--cpu-features", feat]);
         }
     }
 
@@ -2618,7 +2618,11 @@ fn add_native_libs_from_crate(
             NativeLibKind::WasmImportModule => {}
             NativeLibKind::LinkArg => {
                 if link_static {
-                    cmd.linker_arg(OsStr::new(name), verbatim);
+                    if verbatim {
+                        cmd.verbatim_arg(name);
+                    } else {
+                        cmd.link_arg(name);
+                    }
                 }
             }
         }
@@ -2813,6 +2817,15 @@ fn rehome_sysroot_lib_dir(sess: &Session, lib_dir: &Path) -> PathBuf {
     }
 }
 
+fn rehome_lib_path(sess: &Session, path: &Path) -> PathBuf {
+    if let Some(dir) = path.parent() {
+        let file_name = path.file_name().expect("library path has no file name component");
+        rehome_sysroot_lib_dir(sess, dir).join(file_name)
+    } else {
+        fix_windows_verbatim_for_gcc(path)
+    }
+}
+
 // Adds the static "rlib" versions of all crates to the command line.
 // There's a bit of magic which happens here specifically related to LTO,
 // namely that we remove upstream object files.
@@ -2843,15 +2856,8 @@ fn add_static_crate(
     let src = &codegen_results.crate_info.used_crate_source[&cnum];
     let cratepath = &src.rlib.as_ref().unwrap().0;
 
-    let mut link_upstream = |path: &Path| {
-        let rlib_path = if let Some(dir) = path.parent() {
-            let file_name = path.file_name().expect("rlib path has no file name path component");
-            rehome_sysroot_lib_dir(sess, dir).join(file_name)
-        } else {
-            fix_windows_verbatim_for_gcc(path)
-        };
-        cmd.link_staticlib_by_path(&rlib_path, false);
-    };
+    let mut link_upstream =
+        |path: &Path| cmd.link_staticlib_by_path(&rehome_lib_path(sess, path), false);
 
     if !are_upstream_rust_objects_already_included(sess)
         || ignored_for_lto(sess, &codegen_results.crate_info, cnum)
@@ -2915,27 +2921,7 @@ fn add_static_crate(
 
 // Same thing as above, but for dynamic crates instead of static crates.
 fn add_dynamic_crate(cmd: &mut dyn Linker, sess: &Session, cratepath: &Path) {
-    // Just need to tell the linker about where the library lives and
-    // what its name is
-    let parent = cratepath.parent();
-    // When producing a dll, the MSVC linker may not actually emit a
-    // `foo.lib` file if the dll doesn't actually export any symbols, so we
-    // check to see if the file is there and just omit linking to it if it's
-    // not present.
-    if sess.target.is_like_msvc && !cratepath.with_extension("dll.lib").exists() {
-        return;
-    }
-    if let Some(dir) = parent {
-        cmd.include_path(&rehome_sysroot_lib_dir(sess, dir));
-    }
-    // "<dir>/name.dll -> name.dll" on windows-msvc
-    // "<dir>/name.dll -> name" on windows-gnu
-    // "<dir>/libname.<ext> -> name" elsewhere
-    let stem = if sess.target.is_like_msvc { cratepath.file_name() } else { cratepath.file_stem() };
-    let stem = stem.unwrap().to_str().unwrap();
-    // Convert library file-stem into a cc -l argument.
-    let prefix = if stem.starts_with("lib") && !sess.target.is_like_windows { 3 } else { 0 };
-    cmd.link_dylib_by_name(&stem[prefix..], false, true);
+    cmd.link_dylib_by_path(&rehome_lib_path(sess, cratepath), true);
 }
 
 fn relevant_lib(sess: &Session, lib: &NativeLib) -> bool {
@@ -3012,10 +2998,10 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
             // This is admittedly a bit strange, as on most targets
             // `-isysroot` only applies to include header files, but on Apple
             // targets this also applies to libraries and frameworks.
-            cmd.args(&["-isysroot", &sdk_root]);
+            cmd.cc_args(&["-isysroot", &sdk_root]);
         }
         LinkerFlavor::Darwin(Cc::No, _) => {
-            cmd.args(&["-syslibroot", &sdk_root]);
+            cmd.link_args(&["-syslibroot", &sdk_root]);
         }
         _ => unreachable!(),
     }
@@ -3026,8 +3012,9 @@ fn add_apple_sdk(cmd: &mut dyn Linker, sess: &Session, flavor: LinkerFlavor) {
         // search path.
 
         // The flags are called `-L` and `-F` both in Clang, ld64 and ldd.
-        cmd.arg(format!("-L{sdk_root}/System/iOSSupport/usr/lib"));
-        cmd.arg(format!("-F{sdk_root}/System/iOSSupport/System/Library/Frameworks"));
+        let sdk_root = Path::new(&sdk_root);
+        cmd.include_path(&sdk_root.join("System/iOSSupport/usr/lib"));
+        cmd.framework_path(&sdk_root.join("System/iOSSupport/System/Library/Frameworks"));
     }
 }
 
@@ -3142,7 +3129,7 @@ fn add_lld_args(
         for path in sess.get_tools_search_paths(false) {
             let linker_path = path.join("gcc-ld");
             linker_path_exists |= linker_path.exists();
-            cmd.arg({
+            cmd.cc_arg({
                 let mut arg = OsString::from("-B");
                 arg.push(linker_path);
                 arg
@@ -3162,7 +3149,7 @@ fn add_lld_args(
     // is to use LLD but the `wasm32-wasip2` target relies on a wrapper around
     // this, `wasm-component-ld`, which is overridden if this option is passed.
     if !sess.target.is_like_wasm {
-        cmd.arg("-fuse-ld=lld");
+        cmd.cc_arg("-fuse-ld=lld");
     }
 
     if !flavor.is_gnu() {
@@ -3186,7 +3173,7 @@ fn add_lld_args(
         // targeting a different linker flavor on macOS, and that's also always
         // the case when targeting WASM.
         if sess.target.linker_flavor != sess.host.linker_flavor {
-            cmd.arg(format!("--target={}", sess.target.llvm_target));
+            cmd.cc_arg(format!("--target={}", sess.target.llvm_target));
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index a82478900b1..2bd5dfdce83 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -8,7 +8,7 @@ use std::fs::{self, File};
 use std::io::prelude::*;
 use std::io::{self, BufWriter};
 use std::path::{Path, PathBuf};
-use std::{env, mem, str};
+use std::{env, iter, mem, str};
 
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_metadata::find_native_static_library;
@@ -159,6 +159,102 @@ pub fn get_linker<'a>(
     }
 }
 
+// Note: Ideally neither these helper function, nor the macro-generated inherent methods below
+// would exist, and these functions would live in `trait Linker`.
+// Unfortunately, adding these functions to `trait Linker` make it `dyn`-incompatible.
+// If the methods are added to the trait with `where Self: Sized` bounds, then even a separate
+// implementation of them for `dyn Linker {}` wouldn't work due to a conflict with those
+// uncallable methods in the trait.
+
+/// Just pass the arguments to the linker as is.
+/// It is assumed that they are correctly prepared in advance.
+fn verbatim_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>>,
+) -> &mut L {
+    for arg in args {
+        l.cmd().arg(arg);
+    }
+    l
+}
+/// Arguments for the underlying linker.
+/// Add options to pass them through cc wrapper if `Linker` is a cc wrapper.
+fn link_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>,
+) -> &mut L {
+    let args = args.into_iter();
+    if !l.is_cc() {
+        verbatim_args(l, args);
+    } else if args.len() != 0 {
+        // FIXME: Support arguments with commas, see `rpaths_to_flags` for the example.
+        let mut combined_arg = OsString::from("-Wl");
+        for arg in args {
+            combined_arg.push(",");
+            combined_arg.push(arg);
+        }
+        l.cmd().arg(combined_arg);
+    }
+    l
+}
+/// Arguments for the cc wrapper specifically.
+/// Check that it's indeed a cc wrapper and pass verbatim.
+fn cc_args<L: Linker + ?Sized>(l: &mut L, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut L {
+    assert!(l.is_cc());
+    verbatim_args(l, args)
+}
+/// Arguments supported by both underlying linker and cc wrapper, pass verbatim.
+fn link_or_cc_args<L: Linker + ?Sized>(
+    l: &mut L,
+    args: impl IntoIterator<Item: AsRef<OsStr>>,
+) -> &mut L {
+    verbatim_args(l, args)
+}
+
+macro_rules! generate_arg_methods {
+    ($($ty:ty)*) => { $(
+        impl $ty {
+            pub fn verbatim_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                verbatim_args(self, args)
+            }
+            pub fn verbatim_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                verbatim_args(self, iter::once(arg))
+            }
+            pub fn link_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>, IntoIter: ExactSizeIterator>) -> &mut Self {
+                link_args(self, args)
+            }
+            pub fn link_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                link_args(self, iter::once(arg))
+            }
+            pub fn cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                cc_args(self, args)
+            }
+            pub fn cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                cc_args(self, iter::once(arg))
+            }
+            pub fn link_or_cc_args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) -> &mut Self {
+                link_or_cc_args(self, args)
+            }
+            pub fn link_or_cc_arg(&mut self, arg: impl AsRef<OsStr>) -> &mut Self {
+                link_or_cc_args(self, iter::once(arg))
+            }
+        }
+    )* }
+}
+
+generate_arg_methods! {
+    GccLinker<'_>
+    MsvcLinker<'_>
+    EmLinker<'_>
+    WasmLd<'_>
+    L4Bender<'_>
+    AixLinker<'_>
+    LlbcLinker<'_>
+    PtxLinker<'_>
+    BpfLinker<'_>
+    dyn Linker + '_
+}
+
 /// Linker abstraction used by `back::link` to build up the command to invoke a
 /// linker.
 ///
@@ -168,17 +264,33 @@ pub fn get_linker<'a>(
 /// MSVC linker (e.g., `link.exe`) is being used.
 pub trait Linker {
     fn cmd(&mut self) -> &mut Command;
+    fn is_cc(&self) -> bool {
+        false
+    }
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
-    fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, as_needed: bool);
+    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
+        bug!("dylib linked with unsupported linker")
+    }
+    fn link_dylib_by_path(&mut self, _path: &Path, _as_needed: bool) {
+        bug!("dylib linked with unsupported linker")
+    }
     fn link_framework_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
         bug!("framework linked with unsupported linker")
     }
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool);
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool);
-    fn include_path(&mut self, path: &Path);
-    fn framework_path(&mut self, path: &Path);
-    fn output_filename(&mut self, path: &Path);
-    fn add_object(&mut self, path: &Path);
+    fn include_path(&mut self, path: &Path) {
+        link_or_cc_args(link_or_cc_args(self, &["-L"]), &[path]);
+    }
+    fn framework_path(&mut self, _path: &Path) {
+        bug!("framework path set with unsupported linker")
+    }
+    fn output_filename(&mut self, path: &Path) {
+        link_or_cc_args(link_or_cc_args(self, &["-o"]), &[path]);
+    }
+    fn add_object(&mut self, path: &Path) {
+        link_or_cc_args(self, &[path]);
+    }
     fn gc_sections(&mut self, keep_metadata: bool);
     fn no_gc_sections(&mut self);
     fn full_relro(&mut self);
@@ -198,25 +310,9 @@ pub trait Linker {
     fn add_no_exec(&mut self) {}
     fn add_as_needed(&mut self) {}
     fn reset_per_library_state(&mut self) {}
-    fn linker_arg(&mut self, arg: &OsStr, verbatim: bool) {
-        self.linker_args(&[arg], verbatim);
-    }
-    fn linker_args(&mut self, args: &[&OsStr], _verbatim: bool) {
-        args.into_iter().for_each(|a| {
-            self.cmd().arg(a);
-        });
-    }
 }
 
 impl dyn Linker + '_ {
-    pub fn arg(&mut self, arg: impl AsRef<OsStr>) {
-        self.cmd().arg(arg);
-    }
-
-    pub fn args(&mut self, args: impl IntoIterator<Item: AsRef<OsStr>>) {
-        self.cmd().args(args);
-    }
-
     pub fn take_cmd(&mut self) -> Command {
         mem::replace(self.cmd(), Command::new(""))
     }
@@ -233,14 +329,6 @@ pub struct GccLinker<'a> {
 }
 
 impl<'a> GccLinker<'a> {
-    fn linker_arg(&mut self, arg: impl AsRef<OsStr>) {
-        Linker::linker_arg(self, arg.as_ref(), false);
-    }
-    fn linker_args(&mut self, args: &[impl AsRef<OsStr>]) {
-        let args_vec: Vec<&OsStr> = args.iter().map(|x| x.as_ref()).collect();
-        Linker::linker_args(self, &args_vec, false);
-    }
-
     fn takes_hints(&self) -> bool {
         // Really this function only returns true if the underlying linker
         // configured for a compiler is binutils `ld.bfd` and `ld.gold`. We
@@ -262,7 +350,7 @@ impl<'a> GccLinker<'a> {
             return;
         }
         if self.hinted_static != Some(true) {
-            self.linker_arg("-Bstatic");
+            self.link_arg("-Bstatic");
             self.hinted_static = Some(true);
         }
     }
@@ -272,7 +360,7 @@ impl<'a> GccLinker<'a> {
             return;
         }
         if self.hinted_static != Some(false) {
-            self.linker_arg("-Bdynamic");
+            self.link_arg("-Bdynamic");
             self.hinted_static = Some(false);
         }
     }
@@ -281,7 +369,7 @@ impl<'a> GccLinker<'a> {
         if let Some(plugin_path) = plugin_path {
             let mut arg = OsString::from("-plugin=");
             arg.push(plugin_path);
-            self.linker_arg(&arg);
+            self.link_arg(&arg);
         }
 
         let opt_level = match self.sess.opts.optimize {
@@ -292,9 +380,9 @@ impl<'a> GccLinker<'a> {
         };
 
         if let Some(path) = &self.sess.opts.unstable_opts.profile_sample_use {
-            self.linker_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
+            self.link_arg(&format!("-plugin-opt=sample-profile={}", path.display()));
         };
-        self.linker_args(&[
+        self.link_args(&[
             &format!("-plugin-opt={opt_level}"),
             &format!("-plugin-opt=mcpu={}", self.target_cpu),
         ]);
@@ -304,10 +392,10 @@ impl<'a> GccLinker<'a> {
         // On mac we need to tell the linker to let this library be rpathed
         if self.sess.target.is_like_osx {
             if !self.is_ld {
-                self.cmd.arg("-dynamiclib");
+                self.cc_arg("-dynamiclib");
             }
 
-            self.linker_arg("-dylib");
+            self.link_arg("-dylib");
 
             // Note that the `osx_rpath_install_name` option here is a hack
             // purely to support rustbuild right now, we should get a more
@@ -316,105 +404,110 @@ impl<'a> GccLinker<'a> {
             if self.sess.opts.cg.rpath || self.sess.opts.unstable_opts.osx_rpath_install_name {
                 let mut rpath = OsString::from("@rpath/");
                 rpath.push(out_filename.file_name().unwrap());
-                self.linker_args(&[OsString::from("-install_name"), rpath]);
+                self.link_arg("-install_name").link_arg(rpath);
             }
         } else {
-            self.cmd.arg("-shared");
-            if self.sess.target.is_like_windows {
-                // The output filename already contains `dll_suffix` so
-                // the resulting import library will have a name in the
-                // form of libfoo.dll.a
-                let implib_name =
-                    out_filename.file_name().and_then(|file| file.to_str()).map(|file| {
-                        format!(
-                            "{}{}{}",
-                            self.sess.target.staticlib_prefix,
-                            file,
-                            self.sess.target.staticlib_suffix
-                        )
-                    });
-                if let Some(implib_name) = implib_name {
-                    let implib = out_filename.parent().map(|dir| dir.join(&implib_name));
-                    if let Some(implib) = implib {
-                        self.linker_arg(&format!("--out-implib={}", (*implib).to_str().unwrap()));
-                    }
+            self.link_or_cc_arg("-shared");
+            if let Some(name) = out_filename.file_name() {
+                if self.sess.target.is_like_windows {
+                    // The output filename already contains `dll_suffix` so
+                    // the resulting import library will have a name in the
+                    // form of libfoo.dll.a
+                    let mut implib_name = OsString::from(&*self.sess.target.staticlib_prefix);
+                    implib_name.push(name);
+                    implib_name.push(&*self.sess.target.staticlib_suffix);
+                    let mut out_implib = OsString::from("--out-implib=");
+                    out_implib.push(out_filename.with_file_name(implib_name));
+                    self.link_arg(out_implib);
+                } else {
+                    // When dylibs are linked by a full path this value will get into `DT_NEEDED`
+                    // instead of the full path, so the library can be later found in some other
+                    // location than that specific path.
+                    let mut soname = OsString::from("-soname=");
+                    soname.push(name);
+                    self.link_arg(soname);
                 }
             }
         }
     }
-}
 
-impl<'a> Linker for GccLinker<'a> {
-    /// Passes a series of arguments directly to the linker.
-    ///
-    /// When the linker is ld-like, the arguments are simply appended to the command. When the
-    /// linker is not ld-like such as when using a compiler as a linker, the arguments are joined by
-    /// commas to form an argument that is then prepended with `-Wl`. In this situation, only a
-    /// single argument is appended to the command to ensure that the order of the arguments is
-    /// preserved by the compiler.
-    fn linker_args(&mut self, args: &[&OsStr], verbatim: bool) {
-        if self.is_ld || verbatim {
-            args.into_iter().for_each(|a| {
-                self.cmd.arg(a);
-            });
-        } else {
-            if !args.is_empty() {
-                let mut s = OsString::from("-Wl");
-                for a in args {
-                    s.push(",");
-                    s.push(a);
-                }
-                self.cmd.arg(s);
+    fn with_as_needed(&mut self, as_needed: bool, f: impl FnOnce(&mut Self)) {
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // 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_warn(errors::Ld64UnimplementedModifier);
+            } else if self.is_gnu && !self.sess.target.is_like_windows {
+                self.link_arg("--no-as-needed");
+            } else {
+                self.sess.dcx().emit_warn(errors::LinkerUnsupportedModifier);
+            }
+        }
+
+        f(self);
+
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // See above FIXME comment
+            } else if self.is_gnu && !self.sess.target.is_like_windows {
+                self.link_arg("--as-needed");
             }
         }
     }
+}
 
+impl<'a> Linker for GccLinker<'a> {
     fn cmd(&mut self) -> &mut Command {
         &mut self.cmd
     }
 
+    fn is_cc(&self) -> bool {
+        !self.is_ld
+    }
+
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path) {
         match output_kind {
             LinkOutputKind::DynamicNoPicExe => {
                 if !self.is_ld && self.is_gnu {
-                    self.cmd.arg("-no-pie");
+                    self.cc_arg("-no-pie");
                 }
             }
             LinkOutputKind::DynamicPicExe => {
                 // noop on windows w/ gcc & ld, error w/ lld
                 if !self.sess.target.is_like_windows {
                     // `-pie` works for both gcc wrapper and ld.
-                    self.cmd.arg("-pie");
+                    self.link_or_cc_arg("-pie");
                 }
             }
             LinkOutputKind::StaticNoPicExe => {
                 // `-static` works for both gcc wrapper and ld.
-                self.cmd.arg("-static");
+                self.link_or_cc_arg("-static");
                 if !self.is_ld && self.is_gnu {
-                    self.cmd.arg("-no-pie");
+                    self.cc_arg("-no-pie");
                 }
             }
             LinkOutputKind::StaticPicExe => {
                 if !self.is_ld {
                     // Note that combination `-static -pie` doesn't work as expected
                     // for the gcc wrapper, `-static` in that case suppresses `-pie`.
-                    self.cmd.arg("-static-pie");
+                    self.cc_arg("-static-pie");
                 } else {
                     // `--no-dynamic-linker` and `-z text` are not strictly necessary for producing
                     // a static pie, but currently passed because gcc and clang pass them.
                     // The former suppresses the `INTERP` ELF header specifying dynamic linker,
                     // which is otherwise implicitly injected by ld (but not lld).
                     // The latter doesn't change anything, only ensures that everything is pic.
-                    self.cmd.args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);
+                    self.link_args(&["-static", "-pie", "--no-dynamic-linker", "-z", "text"]);
                 }
             }
             LinkOutputKind::DynamicDylib => self.build_dylib(out_filename),
             LinkOutputKind::StaticDylib => {
-                self.cmd.arg("-static");
+                self.link_or_cc_arg("-static");
                 self.build_dylib(out_filename);
             }
             LinkOutputKind::WasiReactorExe => {
-                self.linker_args(&["--entry", "_initialize"]);
+                self.link_args(&["--entry", "_initialize"]);
             }
         }
         // VxWorks compiler driver introduced `--static-crt` flag specifically for rustc,
@@ -430,7 +523,7 @@ impl<'a> Linker for GccLinker<'a> {
                     | LinkOutputKind::StaticDylib
             )
         {
-            self.cmd.arg("--static-crt");
+            self.cc_arg("--static-crt");
         }
     }
 
@@ -443,27 +536,18 @@ impl<'a> Linker for GccLinker<'a> {
             // to the linker.
             return;
         }
-        if !as_needed {
-            if self.sess.target.is_like_osx {
-                // 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_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_warn(errors::LinkerUnsupportedModifier);
-            }
-        }
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{}{name}", if verbatim && self.is_gnu { ":" } else { "" },));
-        if !as_needed {
-            if self.sess.target.is_like_osx {
-                // See above FIXME comment
-            } else if self.is_gnu && !self.sess.target.is_like_windows {
-                self.linker_arg("--as-needed");
-            }
-        }
+        self.with_as_needed(as_needed, |this| {
+            let colon = if verbatim && this.is_gnu { ":" } else { "" };
+            this.link_or_cc_arg(format!("-l{colon}{name}"));
+        });
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, as_needed: bool) {
+        self.hint_dynamic();
+        self.with_as_needed(as_needed, |this| {
+            this.link_or_cc_arg(path);
+        })
     }
 
     fn link_framework_by_name(&mut self, name: &str, _verbatim: bool, as_needed: bool) {
@@ -471,63 +555,51 @@ impl<'a> Linker for GccLinker<'a> {
         if !as_needed {
             // 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(name);
+            // self.link_or_cc_arg("-needed_framework").link_or_cc_arg(name);
             self.sess.dcx().emit_warn(errors::Ld64UnimplementedModifier);
         }
-        self.cmd.arg("-framework").arg(name);
+        self.link_or_cc_args(&["-framework", name]);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         self.hint_static();
         let colon = if verbatim && self.is_gnu { ":" } else { "" };
         if !whole_archive {
-            self.cmd.arg(format!("-l{colon}{name}"));
+            self.link_or_cc_arg(format!("-l{colon}{name}"));
         } else if self.sess.target.is_like_osx {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
-            self.linker_arg("-force_load");
-            self.linker_arg(find_native_static_library(name, verbatim, self.sess));
+            self.link_arg("-force_load");
+            self.link_arg(find_native_static_library(name, verbatim, self.sess));
         } else {
-            self.linker_arg("--whole-archive");
-            self.cmd.arg(format!("-l{colon}{name}"));
-            self.linker_arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_arg(format!("-l{colon}{name}"))
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else if self.sess.target.is_like_osx {
-            self.linker_arg("-force_load");
-            self.linker_arg(path);
+            self.link_arg("-force_load").link_arg(path);
         } else {
-            self.linker_arg("--whole-archive");
-            self.linker_arg(path);
-            self.linker_arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
     fn framework_path(&mut self, path: &Path) {
-        self.cmd.arg("-F").arg(path);
-    }
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_or_cc_arg("-F").link_or_cc_arg(path);
     }
     fn full_relro(&mut self) {
-        self.linker_args(&["-z", "relro", "-z", "now"]);
+        self.link_args(&["-z", "relro", "-z", "now"]);
     }
     fn partial_relro(&mut self) {
-        self.linker_args(&["-z", "relro"]);
+        self.link_args(&["-z", "relro"]);
     }
     fn no_relro(&mut self) {
-        self.linker_args(&["-z", "norelro"]);
+        self.link_args(&["-z", "norelro"]);
     }
 
     fn gc_sections(&mut self, keep_metadata: bool) {
@@ -546,7 +618,7 @@ impl<'a> Linker for GccLinker<'a> {
         // for partial linking when using multiple codegen units (-r). So we
         // insert it here.
         if self.sess.target.is_like_osx {
-            self.linker_arg("-dead_strip");
+            self.link_arg("-dead_strip");
 
         // If we're building a dylib, we don't use --gc-sections because LLVM
         // has already done the best it can do, and we also don't want to
@@ -554,13 +626,13 @@ impl<'a> Linker for GccLinker<'a> {
         // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67%
         // reduction.
         } else if (self.is_gnu || self.sess.target.is_like_wasm) && !keep_metadata {
-            self.linker_arg("--gc-sections");
+            self.link_arg("--gc-sections");
         }
     }
 
     fn no_gc_sections(&mut self) {
         if self.is_gnu || self.sess.target.is_like_wasm {
-            self.linker_arg("--no-gc-sections");
+            self.link_arg("--no-gc-sections");
         }
     }
 
@@ -574,7 +646,7 @@ impl<'a> Linker for GccLinker<'a> {
         if self.sess.opts.optimize == config::OptLevel::Default
             || self.sess.opts.optimize == config::OptLevel::Aggressive
         {
-            self.linker_arg("-O1");
+            self.link_arg("-O1");
         }
     }
 
@@ -594,8 +666,7 @@ impl<'a> Linker for GccLinker<'a> {
         //
         // Though it may be worth to try to revert those changes upstream, since
         // the overhead of the initialization should be minor.
-        self.cmd.arg("-u");
-        self.cmd.arg("__llvm_profile_runtime");
+        self.link_or_cc_args(&["-u", "__llvm_profile_runtime"]);
     }
 
     fn control_flow_guard(&mut self) {}
@@ -616,33 +687,33 @@ impl<'a> Linker for GccLinker<'a> {
                 // The --strip-debug case is handled by running an external
                 // `strip` utility as a separate step after linking.
                 if !self.sess.target.is_like_solaris {
-                    self.linker_arg("--strip-debug");
+                    self.link_arg("--strip-debug");
                 }
             }
             Strip::Symbols => {
-                self.linker_arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
         match self.sess.opts.unstable_opts.debuginfo_compression {
             config::DebugInfoCompression::None => {}
             config::DebugInfoCompression::Zlib => {
-                self.linker_arg("--compress-debug-sections=zlib");
+                self.link_arg("--compress-debug-sections=zlib");
             }
             config::DebugInfoCompression::Zstd => {
-                self.linker_arg("--compress-debug-sections=zstd");
+                self.link_arg("--compress-debug-sections=zstd");
             }
         }
     }
 
     fn no_crt_objects(&mut self) {
         if !self.is_ld {
-            self.cmd.arg("-nostartfiles");
+            self.cc_arg("-nostartfiles");
         }
     }
 
     fn no_default_libraries(&mut self) {
         if !self.is_ld {
-            self.cmd.arg("-nodefaultlibs");
+            self.cc_arg("-nodefaultlibs");
         }
     }
 
@@ -718,24 +789,22 @@ impl<'a> Linker for GccLinker<'a> {
         }
 
         if self.sess.target.is_like_osx {
-            self.linker_args(&[OsString::from("-exported_symbols_list"), path.into()]);
+            self.link_arg("-exported_symbols_list").link_arg(path);
         } else if self.sess.target.is_like_solaris {
-            self.linker_args(&[OsString::from("-M"), path.into()]);
+            self.link_arg("-M").link_arg(path);
         } else {
             if is_windows {
-                self.linker_arg(path);
+                self.link_arg(path);
             } else {
                 let mut arg = OsString::from("--version-script=");
                 arg.push(path);
-                self.linker_arg(arg);
-                self.linker_arg("--no-undefined-version");
+                self.link_arg(arg).link_arg("--no-undefined-version");
             }
         }
     }
 
     fn subsystem(&mut self, subsystem: &str) {
-        self.linker_arg("--subsystem");
-        self.linker_arg(&subsystem);
+        self.link_args(&["--subsystem", subsystem]);
     }
 
     fn reset_per_library_state(&mut self) {
@@ -760,23 +829,23 @@ impl<'a> Linker for GccLinker<'a> {
     // Some versions of `gcc` add it implicitly, some (e.g. `musl-gcc`) don't,
     // so we just always add it.
     fn add_eh_frame_header(&mut self) {
-        self.linker_arg("--eh-frame-hdr");
+        self.link_arg("--eh-frame-hdr");
     }
 
     fn add_no_exec(&mut self) {
         if self.sess.target.is_like_windows {
-            self.linker_arg("--nxcompat");
+            self.link_arg("--nxcompat");
         } else if self.is_gnu {
-            self.linker_args(&["-z", "noexecstack"]);
+            self.link_args(&["-z", "noexecstack"]);
         }
     }
 
     fn add_as_needed(&mut self) {
         if self.is_gnu && !self.sess.target.is_like_windows {
-            self.linker_arg("--as-needed");
+            self.link_arg("--as-needed");
         } else if self.sess.target.is_like_solaris {
             // -z ignore is the Solaris equivalent to the GNU ld --as-needed option
-            self.linker_args(&["-z", "ignore"]);
+            self.link_args(&["-z", "ignore"]);
         }
     }
 }
@@ -798,10 +867,10 @@ impl<'a> Linker for MsvcLinker<'a> {
             | LinkOutputKind::StaticNoPicExe
             | LinkOutputKind::StaticPicExe => {}
             LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
-                self.cmd.arg("/DLL");
+                self.link_arg("/DLL");
                 let mut arg: OsString = "/IMPLIB:".into();
                 arg.push(out_filename.with_extension("dll.lib"));
-                self.cmd.arg(arg);
+                self.link_arg(arg);
             }
             LinkOutputKind::WasiReactorExe => {
                 panic!("can't link as reactor on non-wasi target");
@@ -810,44 +879,49 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn link_dylib_by_name(&mut self, name: &str, verbatim: bool, _as_needed: bool) {
-        self.cmd.arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
+        self.link_arg(format!("{}{}", name, if verbatim { "" } else { ".lib" }));
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        // When producing a dll, MSVC linker may not emit an implib file if the dll doesn't export
+        // any symbols, so we skip linking if the implib file is not present.
+        let implib_path = path.with_extension("dll.lib");
+        if implib_path.exists() {
+            self.link_or_cc_arg(implib_path);
+        }
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         let prefix = if whole_archive { "/WHOLEARCHIVE:" } else { "" };
         let suffix = if verbatim { "" } else { ".lib" };
-        self.cmd.arg(format!("{prefix}{name}{suffix}"));
+        self.link_arg(format!("{prefix}{name}{suffix}"));
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_arg(path);
         } else {
             let mut arg = OsString::from("/WHOLEARCHIVE:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn gc_sections(&mut self, _keep_metadata: bool) {
         // MSVC's ICF (Identical COMDAT Folding) link optimization is
         // slow for Rust and thus we disable it by default when not in
         // optimization build.
         if self.sess.opts.optimize != config::OptLevel::No {
-            self.cmd.arg("/OPT:REF,ICF");
+            self.link_arg("/OPT:REF,ICF");
         } else {
             // It is necessary to specify NOICF here, because /OPT:REF
             // implies ICF by default.
-            self.cmd.arg("/OPT:REF,NOICF");
+            self.link_arg("/OPT:REF,NOICF");
         }
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("/OPT:NOREF,NOICF");
+        self.link_arg("/OPT:NOREF,NOICF");
     }
 
     fn full_relro(&mut self) {
@@ -867,23 +941,19 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("/NODEFAULTLIB");
+        self.link_arg("/NODEFAULTLIB");
     }
 
     fn include_path(&mut self, path: &Path) {
         let mut arg = OsString::from("/LIBPATH:");
         arg.push(path);
-        self.cmd.arg(&arg);
+        self.link_arg(&arg);
     }
 
     fn output_filename(&mut self, path: &Path) {
         let mut arg = OsString::from("/OUT:");
         arg.push(path);
-        self.cmd.arg(&arg);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        bug!("frameworks are not supported on windows")
+        self.link_arg(&arg);
     }
 
     fn optimize(&mut self) {
@@ -895,19 +965,19 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn control_flow_guard(&mut self) {
-        self.cmd.arg("/guard:cf");
+        self.link_arg("/guard:cf");
     }
 
     fn ehcont_guard(&mut self) {
         if self.sess.target.pointer_width == 64 {
-            self.cmd.arg("/guard:ehcont");
+            self.link_arg("/guard:ehcont");
         }
     }
 
     fn debuginfo(&mut self, _strip: Strip, natvis_debugger_visualizers: &[PathBuf]) {
         // This will cause the Microsoft linker to generate a PDB file
         // from the CodeView line tables in the object files.
-        self.cmd.arg("/DEBUG");
+        self.link_arg("/DEBUG");
 
         // Default to emitting only the file name of the PDB file into
         // the binary instead of the full path. Emitting the full path
@@ -916,7 +986,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         //
         // This default behavior can be overridden by explicitly passing
         // `-Clink-arg=/PDBALTPATH:...` to rustc.
-        self.cmd.arg("/PDBALTPATH:%_PDB%");
+        self.link_arg("/PDBALTPATH:%_PDB%");
 
         // This will cause the Microsoft linker to embed .natvis info into the PDB file
         let natvis_dir_path = self.sess.sysroot.join("lib\\rustlib\\etc");
@@ -928,7 +998,7 @@ impl<'a> Linker for MsvcLinker<'a> {
                         if path.extension() == Some("natvis".as_ref()) {
                             let mut arg = OsString::from("/NATVIS:");
                             arg.push(path);
-                            self.cmd.arg(arg);
+                            self.link_arg(arg);
                         }
                     }
                     Err(error) => {
@@ -942,7 +1012,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         for path in natvis_debugger_visualizers {
             let mut arg = OsString::from("/NATVIS:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
@@ -986,13 +1056,13 @@ impl<'a> Linker for MsvcLinker<'a> {
         }
         let mut arg = OsString::from("/DEF:");
         arg.push(path);
-        self.cmd.arg(&arg);
+        self.link_arg(&arg);
     }
 
     fn subsystem(&mut self, subsystem: &str) {
         // Note that previous passes of the compiler validated this subsystem,
         // so we just blindly pass it to the linker.
-        self.cmd.arg(&format!("/SUBSYSTEM:{subsystem}"));
+        self.link_arg(&format!("/SUBSYSTEM:{subsystem}"));
 
         // Windows has two subsystems we're interested in right now, the console
         // and windows subsystems. These both implicitly have different entry
@@ -1009,7 +1079,7 @@ impl<'a> Linker for MsvcLinker<'a> {
         //
         // For more information see RFC #1665
         if subsystem == "windows" {
-            self.cmd.arg("/ENTRY:mainCRTStartup");
+            self.link_arg("/ENTRY:mainCRTStartup");
         }
     }
 
@@ -1018,7 +1088,7 @@ impl<'a> Linker for MsvcLinker<'a> {
     }
 
     fn add_no_exec(&mut self) {
-        self.cmd.arg("/NXCOMPAT");
+        self.link_arg("/NXCOMPAT");
     }
 }
 
@@ -1032,31 +1102,27 @@ impl<'a> Linker for EmLinker<'a> {
         &mut self.cmd
     }
 
+    fn is_cc(&self) -> bool {
+        true
+    }
+
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
         // Emscripten always links statically
-        self.cmd.arg("-l").arg(name);
+        self.link_or_cc_args(&["-l", name]);
     }
 
-    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
-        self.cmd.arg("-l").arg(name);
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.link_or_cc_arg(path);
     }
 
-    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
+    fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, _whole_archive: bool) {
+        self.link_or_cc_args(&["-l", name]);
     }
 
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+    fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
+        self.link_or_cc_arg(path);
     }
 
     fn full_relro(&mut self) {
@@ -1071,10 +1137,6 @@ impl<'a> Linker for EmLinker<'a> {
         // noop
     }
 
-    fn framework_path(&mut self, _path: &Path) {
-        bug!("frameworks are not supported on Emscripten")
-    }
-
     fn gc_sections(&mut self, _keep_metadata: bool) {
         // noop
     }
@@ -1085,7 +1147,7 @@ impl<'a> Linker for EmLinker<'a> {
 
     fn optimize(&mut self) {
         // Emscripten performs own optimizations
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.cc_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1106,7 +1168,7 @@ impl<'a> Linker for EmLinker<'a> {
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
         // Preserve names or generate source maps depending on debug info
         // For more information see https://emscripten.org/docs/tools_reference/emcc.html#emcc-g
-        self.cmd.arg(match self.sess.opts.debuginfo {
+        self.cc_arg(match self.sess.opts.debuginfo {
             DebugInfo::None => "-g0",
             DebugInfo::Limited | DebugInfo::LineTablesOnly | DebugInfo::LineDirectivesOnly => {
                 "--profiling-funcs"
@@ -1118,13 +1180,13 @@ impl<'a> Linker for EmLinker<'a> {
     fn no_crt_objects(&mut self) {}
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("-nodefaultlibs");
+        self.cc_arg("-nodefaultlibs");
     }
 
     fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
         debug!("EXPORTED SYMBOLS:");
 
-        self.cmd.arg("-s");
+        self.cc_arg("-s");
 
         let mut arg = OsString::from("EXPORTED_FUNCTIONS=");
         let encoded = serde_json::to_string(
@@ -1135,7 +1197,7 @@ impl<'a> Linker for EmLinker<'a> {
 
         arg.push(encoded);
 
-        self.cmd.arg(arg);
+        self.cc_arg(arg);
     }
 
     fn subsystem(&mut self, _subsystem: &str) {
@@ -1153,7 +1215,7 @@ pub struct WasmLd<'a> {
 }
 
 impl<'a> WasmLd<'a> {
-    fn new(mut cmd: Command, sess: &'a Session) -> WasmLd<'a> {
+    fn new(cmd: Command, sess: &'a Session) -> WasmLd<'a> {
         // If the atomics feature is enabled for wasm then we need a whole bunch
         // of flags:
         //
@@ -1172,18 +1234,19 @@ impl<'a> WasmLd<'a> {
         // On wasm32-unknown-unknown, we also export symbols for glue code to use:
         //    * `--export=*tls*` - when `#[thread_local]` symbols are used these
         //      symbols are how the TLS segments are initialized and configured.
+        let mut wasm_ld = WasmLd { cmd, sess };
         if sess.target_features.contains(&sym::atomics) {
-            cmd.arg("--shared-memory");
-            cmd.arg("--max-memory=1073741824");
-            cmd.arg("--import-memory");
+            wasm_ld.link_args(&["--shared-memory", "--max-memory=1073741824", "--import-memory"]);
             if sess.target.os == "unknown" {
-                cmd.arg("--export=__wasm_init_tls");
-                cmd.arg("--export=__tls_size");
-                cmd.arg("--export=__tls_align");
-                cmd.arg("--export=__tls_base");
+                wasm_ld.link_args(&[
+                    "--export=__wasm_init_tls",
+                    "--export=__tls_size",
+                    "--export=__tls_align",
+                    "--export=__tls_base",
+                ]);
             }
         }
-        WasmLd { cmd, sess }
+        wasm_ld
     }
 }
 
@@ -1199,51 +1262,40 @@ impl<'a> Linker for WasmLd<'a> {
             | LinkOutputKind::StaticNoPicExe
             | LinkOutputKind::StaticPicExe => {}
             LinkOutputKind::DynamicDylib | LinkOutputKind::StaticDylib => {
-                self.cmd.arg("--no-entry");
+                self.link_arg("--no-entry");
             }
             LinkOutputKind::WasiReactorExe => {
-                self.cmd.arg("--entry");
-                self.cmd.arg("_initialize");
+                self.link_args(&["--entry", "_initialize"]);
             }
         }
     }
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
-        self.cmd.arg("-l").arg(name);
+        self.link_or_cc_args(&["-l", name]);
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.link_or_cc_arg(path);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg("-l").arg(name);
+            self.link_or_cc_args(&["-l", name]);
         } else {
-            self.cmd.arg("--whole-archive").arg("-l").arg(name).arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_args(&["-l", name])
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
-            self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1251,17 +1303,17 @@ impl<'a> Linker for WasmLd<'a> {
     fn no_relro(&mut self) {}
 
     fn gc_sections(&mut self, _keep_metadata: bool) {
-        self.cmd.arg("--gc-sections");
+        self.link_arg("--gc-sections");
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("--no-gc-sections");
+        self.link_arg("--no-gc-sections");
     }
 
     fn optimize(&mut self) {
         // The -O flag is, as of late 2023, only used for merging of strings and debuginfo, and
         // only differentiates -O0 and -O1. It does not apply to LTO.
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.link_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1279,10 +1331,10 @@ impl<'a> Linker for WasmLd<'a> {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
-                self.cmd.arg("--strip-debug");
+                self.link_arg("--strip-debug");
             }
             Strip::Symbols => {
-                self.cmd.arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
     }
@@ -1297,7 +1349,7 @@ impl<'a> Linker for WasmLd<'a> {
 
     fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) {
         for sym in symbols {
-            self.cmd.arg("--export").arg(&sym);
+            self.link_args(&["--export", sym]);
         }
 
         // LLD will hide these otherwise-internal symbols since it only exports
@@ -1305,8 +1357,7 @@ impl<'a> Linker for WasmLd<'a> {
         // others. Various bits and pieces of wasm32-unknown-unknown tooling use
         // this, so be sure these symbols make their way out of the linker as well.
         if self.sess.target.os == "unknown" {
-            self.cmd.arg("--export=__heap_base");
-            self.cmd.arg("--export=__data_end");
+            self.link_args(&["--export=__heap_base", "--export=__data_end"]);
         }
     }
 
@@ -1337,7 +1388,7 @@ impl<'a> WasmLd<'a> {
             // wasm-ld only handles integer LTO opt levels. Use O2
             config::OptLevel::Size | config::OptLevel::SizeMin => "O2",
         };
-        self.cmd.arg(&format!("--lto-{opt_level}"));
+        self.link_arg(&format!("--lto-{opt_level}"));
     }
 }
 
@@ -1355,63 +1406,46 @@ impl<'a> Linker for L4Bender<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        bug!("dylibs are not supported on L4Re");
-    }
-
     fn link_staticlib_by_name(&mut self, name: &str, _verbatim: bool, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(format!("-PC{name}"));
+            self.link_arg(format!("-PC{name}"));
         } else {
-            self.cmd.arg("--whole-archive").arg(format!("-l{name}")).arg("--no-whole-archive");
+            self.link_arg("--whole-archive")
+                .link_or_cc_arg(format!("-l{name}"))
+                .link_arg("--no-whole-archive");
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
-            self.cmd.arg("--whole-archive").arg(path).arg("--no-whole-archive");
+            self.link_arg("--whole-archive").link_or_cc_arg(path).link_arg("--no-whole-archive");
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-    fn framework_path(&mut self, _: &Path) {
-        bug!("frameworks are not supported on L4Re");
-    }
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {
-        self.cmd.arg("-z").arg("relro");
-        self.cmd.arg("-z").arg("now");
+        self.link_args(&["-z", "relro", "-z", "now"]);
     }
 
     fn partial_relro(&mut self) {
-        self.cmd.arg("-z").arg("relro");
+        self.link_args(&["-z", "relro"]);
     }
 
     fn no_relro(&mut self) {
-        self.cmd.arg("-z").arg("norelro");
+        self.link_args(&["-z", "norelro"]);
     }
 
     fn gc_sections(&mut self, keep_metadata: bool) {
         if !keep_metadata {
-            self.cmd.arg("--gc-sections");
+            self.link_arg("--gc-sections");
         }
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("--no-gc-sections");
+        self.link_arg("--no-gc-sections");
     }
 
     fn optimize(&mut self) {
@@ -1420,7 +1454,7 @@ impl<'a> Linker for L4Bender<'a> {
         if self.sess.opts.optimize == config::OptLevel::Default
             || self.sess.opts.optimize == config::OptLevel::Aggressive
         {
-            self.cmd.arg("-O1");
+            self.link_arg("-O1");
         }
     }
 
@@ -1430,16 +1464,16 @@ impl<'a> Linker for L4Bender<'a> {
         match strip {
             Strip::None => {}
             Strip::Debuginfo => {
-                self.cmd().arg("--strip-debug");
+                self.link_arg("--strip-debug");
             }
             Strip::Symbols => {
-                self.cmd().arg("--strip-all");
+                self.link_arg("--strip-all");
             }
         }
     }
 
     fn no_default_libraries(&mut self) {
-        self.cmd.arg("-nostdlib");
+        self.cc_arg("-nostdlib");
     }
 
     fn export_symbols(&mut self, _: &Path, _: CrateType, _: &[String]) {
@@ -1449,7 +1483,7 @@ impl<'a> Linker for L4Bender<'a> {
     }
 
     fn subsystem(&mut self, subsystem: &str) {
-        self.cmd.arg(&format!("--subsystem {subsystem}"));
+        self.link_arg(&format!("--subsystem {subsystem}"));
     }
 
     fn reset_per_library_state(&mut self) {
@@ -1467,12 +1501,12 @@ impl<'a> Linker for L4Bender<'a> {
 
 impl<'a> L4Bender<'a> {
     pub fn new(cmd: Command, sess: &'a Session) -> L4Bender<'a> {
-        L4Bender { cmd: cmd, sess: sess, hinted_static: false }
+        L4Bender { cmd, sess: sess, hinted_static: false }
     }
 
     fn hint_static(&mut self) {
         if !self.hinted_static {
-            self.cmd.arg("-static");
+            self.link_or_cc_arg("-static");
             self.hinted_static = true;
         }
     }
@@ -1487,29 +1521,28 @@ pub struct AixLinker<'a> {
 
 impl<'a> AixLinker<'a> {
     pub fn new(cmd: Command, sess: &'a Session) -> AixLinker<'a> {
-        AixLinker { cmd: cmd, sess: sess, hinted_static: None }
+        AixLinker { cmd, sess: sess, hinted_static: None }
     }
 
     fn hint_static(&mut self) {
         if self.hinted_static != Some(true) {
-            self.cmd.arg("-bstatic");
+            self.link_arg("-bstatic");
             self.hinted_static = Some(true);
         }
     }
 
     fn hint_dynamic(&mut self) {
         if self.hinted_static != Some(false) {
-            self.cmd.arg("-bdynamic");
+            self.link_arg("-bdynamic");
             self.hinted_static = Some(false);
         }
     }
 
     fn build_dylib(&mut self, _out_filename: &Path) {
-        self.cmd.arg("-bM:SRE");
-        self.cmd.arg("-bnoentry");
+        self.link_args(&["-bM:SRE", "-bnoentry"]);
         // FIXME: Use CreateExportList utility to create export list
         // and remove -bexpfull.
-        self.cmd.arg("-bexpfull");
+        self.link_arg("-bexpfull");
     }
 }
 
@@ -1534,47 +1567,36 @@ impl<'a> Linker for AixLinker<'a> {
 
     fn link_dylib_by_name(&mut self, name: &str, _verbatim: bool, _as_needed: bool) {
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{name}"));
+        self.link_or_cc_arg(format!("-l{name}"));
+    }
+
+    fn link_dylib_by_path(&mut self, path: &Path, _as_needed: bool) {
+        self.hint_dynamic();
+        self.link_or_cc_arg(path);
     }
 
     fn link_staticlib_by_name(&mut self, name: &str, verbatim: bool, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(format!("-l{name}"));
+            self.link_or_cc_arg(format!("-l{name}"));
         } else {
             let mut arg = OsString::from("-bkeepfile:");
             arg.push(find_native_static_library(name, verbatim, self.sess));
-            self.cmd.arg(arg);
+            self.link_or_cc_arg(arg);
         }
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, whole_archive: bool) {
         self.hint_static();
         if !whole_archive {
-            self.cmd.arg(path);
+            self.link_or_cc_arg(path);
         } else {
             let mut arg = OsString::from("-bkeepfile:");
             arg.push(path);
-            self.cmd.arg(arg);
+            self.link_arg(arg);
         }
     }
 
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
-    }
-
-    fn framework_path(&mut self, _: &Path) {
-        bug!("frameworks are not supported on AIX");
-    }
-
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1582,17 +1604,17 @@ impl<'a> Linker for AixLinker<'a> {
     fn no_relro(&mut self) {}
 
     fn gc_sections(&mut self, _keep_metadata: bool) {
-        self.cmd.arg("-bgc");
+        self.link_arg("-bgc");
     }
 
     fn no_gc_sections(&mut self) {
-        self.cmd.arg("-bnogc");
+        self.link_arg("-bnogc");
     }
 
     fn optimize(&mut self) {}
 
     fn pgo_gen(&mut self) {
-        self.cmd.arg("-bdbg:namedsects:ss");
+        self.link_arg("-bdbg:namedsects:ss");
     }
 
     fn control_flow_guard(&mut self) {}
@@ -1618,7 +1640,7 @@ impl<'a> Linker for AixLinker<'a> {
         if let Err(e) = res {
             self.sess.dcx().fatal(format!("failed to write export file: {e}"));
         }
-        self.cmd.arg(format!("-bE:{}", path.to_str().unwrap()));
+        self.link_arg(format!("-bE:{}", path.to_str().unwrap()));
     }
 
     fn subsystem(&mut self, _subsystem: &str) {}
@@ -1738,48 +1760,32 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg("--rlib").arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_arg("--rlib").link_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
+        self.link_arg("--debug");
     }
 
     fn add_object(&mut self, path: &Path) {
-        self.cmd.arg("--bitcode").arg(path);
+        self.link_arg("--bitcode").link_arg(path);
     }
 
     fn optimize(&mut self) {
         match self.sess.lto() {
             Lto::Thin | Lto::Fat | Lto::ThinLocal => {
-                self.cmd.arg("-Olto");
+                self.link_arg("-Olto");
             }
 
             Lto::No => {}
         }
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1820,28 +1826,16 @@ impl<'a> Linker for LlbcLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_or_cc_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_arg("--debug");
     }
 
     fn optimize(&mut self) {
@@ -1855,14 +1849,6 @@ impl<'a> Linker for LlbcLinker<'a> {
         };
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1887,7 +1873,7 @@ impl<'a> Linker for LlbcLinker<'a> {
         match _crate_type {
             CrateType::Cdylib => {
                 for sym in symbols {
-                    self.cmd.arg("--export-symbol").arg(sym);
+                    self.link_args(&["--export-symbol", sym]);
                 }
             }
             _ => (),
@@ -1911,32 +1897,20 @@ impl<'a> Linker for BpfLinker<'a> {
 
     fn set_output_kind(&mut self, _output_kind: LinkOutputKind, _out_filename: &Path) {}
 
-    fn link_dylib_by_name(&mut self, _name: &str, _verbatim: bool, _as_needed: bool) {
-        panic!("external dylibs not supported")
-    }
-
     fn link_staticlib_by_name(&mut self, _name: &str, _verbatim: bool, _whole_archive: bool) {
         panic!("staticlibs not supported")
     }
 
     fn link_staticlib_by_path(&mut self, path: &Path, _whole_archive: bool) {
-        self.cmd.arg(path);
-    }
-
-    fn include_path(&mut self, path: &Path) {
-        self.cmd.arg("-L").arg(path);
+        self.link_or_cc_arg(path);
     }
 
     fn debuginfo(&mut self, _strip: Strip, _: &[PathBuf]) {
-        self.cmd.arg("--debug");
-    }
-
-    fn add_object(&mut self, path: &Path) {
-        self.cmd.arg(path);
+        self.link_arg("--debug");
     }
 
     fn optimize(&mut self) {
-        self.cmd.arg(match self.sess.opts.optimize {
+        self.link_arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
             OptLevel::Less => "-O1",
             OptLevel::Default => "-O2",
@@ -1946,14 +1920,6 @@ impl<'a> Linker for BpfLinker<'a> {
         });
     }
 
-    fn output_filename(&mut self, path: &Path) {
-        self.cmd.arg("-o").arg(path);
-    }
-
-    fn framework_path(&mut self, _path: &Path) {
-        panic!("frameworks not supported")
-    }
-
     fn full_relro(&mut self) {}
 
     fn partial_relro(&mut self) {}
@@ -1985,7 +1951,7 @@ impl<'a> Linker for BpfLinker<'a> {
         if let Err(error) = res {
             self.sess.dcx().emit_fatal(errors::SymbolFileWriteFailure { error });
         } else {
-            self.cmd.arg("--export-symbols").arg(&path);
+            self.link_arg("--export-symbols").link_arg(&path);
         }
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs
index f499bbcf853..82070d4453b 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs
@@ -9,16 +9,10 @@ pub struct RPathConfig<'a> {
     pub libs: &'a [&'a Path],
     pub out_filename: PathBuf,
     pub is_like_osx: bool,
-    pub has_rpath: bool,
     pub linker_is_gnu: bool,
 }
 
 pub fn get_rpath_flags(config: &RPathConfig<'_>) -> Vec<OsString> {
-    // No rpath on windows
-    if !config.has_rpath {
-        return Vec::new();
-    }
-
     debug!("preparing the RPATH!");
 
     let rpaths = get_rpaths(config);
diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
index 0de90a1036e..c620e92db1f 100644
--- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
+++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs
@@ -37,7 +37,6 @@ fn test_rpath_relative() {
     if cfg!(target_os = "macos") {
         let config = &mut RPathConfig {
             libs: &[],
-            has_rpath: true,
             is_like_osx: true,
             linker_is_gnu: false,
             out_filename: PathBuf::from("bin/rustc"),
@@ -48,7 +47,6 @@ fn test_rpath_relative() {
         let config = &mut RPathConfig {
             libs: &[],
             out_filename: PathBuf::from("bin/rustc"),
-            has_rpath: true,
             is_like_osx: false,
             linker_is_gnu: true,
         };
@@ -62,7 +60,6 @@ fn test_rpath_relative_issue_119571() {
     let config = &mut RPathConfig {
         libs: &[],
         out_filename: PathBuf::from("rustc"),
-        has_rpath: true,
         is_like_osx: false,
         linker_is_gnu: true,
     };
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index c18816533a2..137f14fe706 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -37,7 +37,7 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::config::{self, CrateType, EntryFnType, OptLevel, OutputType};
 use rustc_session::Session;
 use rustc_span::symbol::sym;
-use rustc_span::Symbol;
+use rustc_span::{Symbol, DUMMY_SP};
 use rustc_target::abi::FIRST_VARIANT;
 
 use std::cmp;
@@ -467,6 +467,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
                 ty::ParamEnv::reveal_all(),
                 start_def_id,
                 cx.tcx().mk_args(&[main_ret_ty.into()]),
+                DUMMY_SP,
             );
             let start_fn = cx.get_fn_addr(start_instance);
 
diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
index d224695d1f2..56a893738df 100644
--- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
+++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs
@@ -1,11 +1,13 @@
 use rustc_ast::{ast, attr, MetaItemKind, NestedMetaItem};
 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
-use rustc_errors::{codes::*, struct_span_code_err};
+use rustc_errors::{codes::*, struct_span_code_err, DiagMessage, SubdiagMessage};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::{lang_items, weak_lang_items::WEAK_LANG_ITEMS, LangItem};
-use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::middle::codegen_fn_attrs::{
+    CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
+};
 use rustc_middle::mir::mono::Linkage;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self as ty, TyCtxt};
@@ -447,6 +449,80 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
                     None
                 };
             }
+            sym::patchable_function_entry => {
+                codegen_fn_attrs.patchable_function_entry = attr.meta_item_list().and_then(|l| {
+                    let mut prefix = None;
+                    let mut entry = None;
+                    for item in l {
+                        let Some(meta_item) = item.meta_item() else {
+                            tcx.dcx().span_err(item.span(), "expected name value pair");
+                            continue;
+                        };
+
+                        let Some(name_value_lit) = meta_item.name_value_literal() else {
+                            tcx.dcx().span_err(item.span(), "expected name value pair");
+                            continue;
+                        };
+
+                        fn emit_error_with_label(
+                            tcx: TyCtxt<'_>,
+                            span: Span,
+                            error: impl Into<DiagMessage>,
+                            label: impl Into<SubdiagMessage>,
+                        ) {
+                            let mut err: rustc_errors::Diag<'_, _> =
+                                tcx.dcx().struct_span_err(span, error);
+                            err.span_label(span, label);
+                            err.emit();
+                        }
+
+                        let attrib_to_write = match meta_item.name_or_empty() {
+                            sym::prefix_nops => &mut prefix,
+                            sym::entry_nops => &mut entry,
+                            _ => {
+                                emit_error_with_label(
+                                    tcx,
+                                    item.span(),
+                                    "unexpected parameter name",
+                                    format!("expected {} or {}", sym::prefix_nops, sym::entry_nops),
+                                );
+                                continue;
+                            }
+                        };
+
+                        let rustc_ast::LitKind::Int(val, _) = name_value_lit.kind else {
+                            emit_error_with_label(
+                                tcx,
+                                name_value_lit.span,
+                                "invalid literal value",
+                                "value must be an integer between `0` and `255`",
+                            );
+                            continue;
+                        };
+
+                        let Ok(val) = val.get().try_into() else {
+                            emit_error_with_label(
+                                tcx,
+                                name_value_lit.span,
+                                "integer value out of range",
+                                "value must be between `0` and `255`",
+                            );
+                            continue;
+                        };
+
+                        *attrib_to_write = Some(val);
+                    }
+
+                    if let (None, None) = (prefix, entry) {
+                        tcx.dcx().span_err(attr.span, "must specify at least one parameter");
+                    }
+
+                    Some(PatchableFunctionEntry::from_prefix_and_entry(
+                        prefix.unwrap_or(0),
+                        entry.unwrap_or(0),
+                    ))
+                })
+            }
             _ => {}
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs
index ddc6797388e..febc8ee2be2 100644
--- a/compiler/rustc_codegen_ssa/src/meth.rs
+++ b/compiler/rustc_codegen_ssa/src/meth.rs
@@ -133,6 +133,7 @@ pub fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>(
     let align = cx.data_layout().pointer_align.abi;
     let vtable = cx.static_addr_of(vtable_const, align, Some("vtable"));
 
+    cx.apply_vcall_visibility_metadata(ty, trait_ref, vtable);
     cx.create_vtable_debuginfo(ty, trait_ref, vtable);
     cx.vtables().borrow_mut().insert((ty, trait_ref), vtable);
     vtable
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 0577ba32ffd..ac2b6ca4e95 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -281,6 +281,7 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
                 | TerminatorKind::UnwindResume
                 | TerminatorKind::UnwindTerminate(_)
                 | TerminatorKind::Return
+                | TerminatorKind::TailCall { .. }
                 | TerminatorKind::CoroutineDrop
                 | TerminatorKind::Unreachable
                 | TerminatorKind::SwitchInt { .. }
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 57138d3b9db..6a5525dc2b3 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -403,7 +403,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             //
             // Why only in unoptimized builds?
             // - In unoptimized builds LLVM uses FastISel which does not support switches, so it
-            //   must fall back to the to the slower SelectionDAG isel. Therefore, using `br` gives
+            //   must fall back to the slower SelectionDAG isel. Therefore, using `br` gives
             //   significant compile time speedups for unoptimized builds.
             // - In optimized builds the above doesn't hold, and using `br` sometimes results in
             //   worse generated code because LLVM can no longer tell that the value being switched
@@ -658,7 +658,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // with #[rustc_inherit_overflow_checks] and inlined from
         // another crate (mostly core::num generic/#[inline] fns),
         // while the current crate doesn't use overflow checks.
-        if !bx.cx().check_overflow() && msg.is_optional_overflow_check() {
+        if !bx.sess().overflow_checks() && msg.is_optional_overflow_check() {
             const_cond = Some(expected);
         }
 
@@ -751,7 +751,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         &mut self,
         helper: &TerminatorCodegenHelper<'tcx>,
         bx: &mut Bx,
-        intrinsic: Option<ty::IntrinsicDef>,
+        intrinsic: ty::IntrinsicDef,
         instance: Option<Instance<'tcx>>,
         source_info: mir::SourceInfo,
         target: Option<mir::BasicBlock>,
@@ -761,8 +761,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         // Emit a panic or a no-op for `assert_*` intrinsics.
         // These are intrinsics that compile to panics so that we can get a message
         // which mentions the offending type, even from a const context.
-        let panic_intrinsic = intrinsic.and_then(|i| ValidityRequirement::from_intrinsic(i.name));
-        if let Some(requirement) = panic_intrinsic {
+        if let Some(requirement) = ValidityRequirement::from_intrinsic(intrinsic.name) {
             let ty = instance.unwrap().args.type_at(0);
 
             let do_panic = !bx
@@ -843,6 +842,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                         ty::ParamEnv::reveal_all(),
                         def_id,
                         args,
+                        fn_span,
                     )
                     .polymorphize(bx.tcx()),
                 ),
@@ -869,12 +869,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
         let sig = callee.layout.ty.fn_sig(bx.tcx());
         let abi = sig.abi();
 
-        // Handle intrinsics old codegen wants Expr's for, ourselves.
-        let intrinsic = match def {
-            Some(ty::InstanceKind::Intrinsic(def_id)) => Some(bx.tcx().intrinsic(def_id).unwrap()),
-            _ => None,
-        };
-
         let extra_args = &args[sig.inputs().skip_binder().len()..];
         let extra_args = bx.tcx().mk_type_list_from_iter(extra_args.iter().map(|op_arg| {
             let op_ty = op_arg.node.ty(self.mir, bx.tcx());
@@ -886,50 +880,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
             None => bx.fn_abi_of_fn_ptr(sig, extra_args),
         };
 
-        if let Some(merging_succ) = self.codegen_panic_intrinsic(
-            &helper,
-            bx,
-            intrinsic,
-            instance,
-            source_info,
-            target,
-            unwind,
-            mergeable_succ,
-        ) {
-            return merging_succ;
-        }
-
         // The arguments we'll be passing. Plus one to account for outptr, if used.
         let arg_count = fn_abi.args.len() + fn_abi.ret.is_indirect() as usize;
 
-        if matches!(intrinsic, Some(ty::IntrinsicDef { name: sym::caller_location, .. })) {
-            return if let Some(target) = target {
-                let location =
-                    self.get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
-
-                let mut llargs = Vec::with_capacity(arg_count);
-                let ret_dest = self.make_return_dest(
+        let instance = match def {
+            Some(ty::InstanceKind::Intrinsic(def_id)) => {
+                let intrinsic = bx.tcx().intrinsic(def_id).unwrap();
+                if let Some(merging_succ) = self.codegen_panic_intrinsic(
+                    &helper,
                     bx,
-                    destination,
-                    &fn_abi.ret,
-                    &mut llargs,
                     intrinsic,
-                    Some(target),
-                );
-                assert_eq!(llargs, []);
-                if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
-                    location.val.store(bx, tmp);
+                    instance,
+                    source_info,
+                    target,
+                    unwind,
+                    mergeable_succ,
+                ) {
+                    return merging_succ;
                 }
-                self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate());
-                helper.funclet_br(self, bx, target, mergeable_succ)
-            } else {
-                MergingSucc::False
-            };
-        }
 
-        let instance = match intrinsic {
-            None => instance,
-            Some(intrinsic) => {
                 let mut llargs = Vec::with_capacity(1);
                 let ret_dest = self.make_return_dest(
                     bx,
@@ -971,6 +940,18 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     })
                     .collect();
 
+                if matches!(intrinsic, ty::IntrinsicDef { name: sym::caller_location, .. }) {
+                    let location = self
+                        .get_caller_location(bx, mir::SourceInfo { span: fn_span, ..source_info });
+
+                    assert_eq!(llargs, []);
+                    if let ReturnDest::IndirectOperand(tmp, _) = ret_dest {
+                        location.val.store(bx, tmp);
+                    }
+                    self.store_return(bx, ret_dest, &fn_abi.ret, location.immediate());
+                    return helper.funclet_br(self, bx, target.unwrap(), mergeable_succ);
+                }
+
                 let instance = *instance.as_ref().unwrap();
                 match Self::codegen_intrinsic_call(bx, instance, fn_abi, &args, dest, span) {
                     Ok(()) => {
@@ -997,6 +978,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
             }
+            _ => instance,
         };
 
         let mut llargs = Vec::with_capacity(arg_count);
@@ -1407,6 +1389,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 fn_span,
                 mergeable_succ(),
             ),
+            mir::TerminatorKind::TailCall { .. } => {
+                // FIXME(explicit_tail_calls): implement tail calls in ssa backend
+                span_bug!(
+                    terminator.source_info.span,
+                    "`TailCall` terminator is not yet supported by `rustc_codegen_ssa`"
+                )
+            }
             mir::TerminatorKind::CoroutineDrop | mir::TerminatorKind::Yield { .. } => {
                 bug!("coroutine ops in codegen")
             }
@@ -1540,7 +1529,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                 //   when passed by value, making it smaller.
                 // - On some ABIs, the Rust layout { u16, u16, u16 } may be padded up to 8 bytes
                 //   when passed by value, making it larger.
-                let copy_bytes = cmp::min(scratch_size.bytes(), arg.layout.size.bytes());
+                let copy_bytes = cmp::min(cast.unaligned_size(bx).bytes(), arg.layout.size.bytes());
                 // Allocate some scratch space...
                 let llscratch = bx.alloca(scratch_size, scratch_align);
                 bx.lifetime_start(llscratch, scratch_size);
diff --git a/compiler/rustc_codegen_ssa/src/mir/locals.rs b/compiler/rustc_codegen_ssa/src/mir/locals.rs
index a6c873e195e..5190021c005 100644
--- a/compiler/rustc_codegen_ssa/src/mir/locals.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/locals.rs
@@ -47,7 +47,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     let expected_ty = self.monomorphize(self.mir.local_decls[local].ty);
                     if expected_ty != op.layout.ty {
                         warn!(
-                            "Unexpected initial operand type: expected {expected_ty:?}, found {:?}.\
+                            "Unexpected initial operand type:\nexpected {expected_ty:?},\nfound    {:?}.\n\
                             See <https://github.com/rust-lang/rust/issues/114858>.",
                             op.layout.ty
                         );
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index e8da9842882..61f57c9030a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -230,10 +230,20 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
             let layout = start_bx.layout_of(fx.monomorphize(decl.ty));
             assert!(!layout.ty.has_erasable_regions());
 
-            if local == mir::RETURN_PLACE && fx.fn_abi.ret.is_indirect() {
-                debug!("alloc: {:?} (return place) -> place", local);
-                let llretptr = start_bx.get_param(0);
-                return LocalRef::Place(PlaceRef::new_sized(llretptr, layout));
+            if local == mir::RETURN_PLACE {
+                match fx.fn_abi.ret.mode {
+                    PassMode::Indirect { .. } => {
+                        debug!("alloc: {:?} (return place) -> place", local);
+                        let llretptr = start_bx.get_param(0);
+                        return LocalRef::Place(PlaceRef::new_sized(llretptr, layout));
+                    }
+                    PassMode::Cast { ref cast, .. } => {
+                        debug!("alloc: {:?} (return place) -> place", local);
+                        let size = cast.size(&start_bx);
+                        return LocalRef::Place(PlaceRef::alloca_size(&mut start_bx, size, layout));
+                    }
+                    _ => {}
+                };
             }
 
             if memory_locals.contains(local) {
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 449fd9ae0db..97d5bb83128 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -109,8 +109,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         bx: &mut Bx,
         layout: TyAndLayout<'tcx>,
     ) -> Self {
+        Self::alloca_size(bx, layout.size, layout)
+    }
+
+    pub fn alloca_size<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
+        bx: &mut Bx,
+        size: Size,
+        layout: TyAndLayout<'tcx>,
+    ) -> Self {
         assert!(layout.is_sized(), "tried to statically allocate unsized place");
-        PlaceValue::alloca(bx, layout.size, layout.align.abi).with_type(layout)
+        PlaceValue::alloca(bx, size, layout.align.abi).with_type(layout)
     }
 
     /// Returns a place for an indirect reference to an unsized place.
diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs
index e8b9490d401..3770bd11cf9 100644
--- a/compiler/rustc_codegen_ssa/src/traits/backend.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs
@@ -22,8 +22,6 @@ use rustc_session::{
 use rustc_span::symbol::Symbol;
 use rustc_target::abi::call::FnAbi;
 
-use std::fmt;
-
 pub trait BackendTypes {
     type Value: CodegenObject;
     type Function: CodegenObject;
@@ -62,7 +60,7 @@ pub trait CodegenBackend {
     fn locale_resource(&self) -> &'static str;
 
     fn init(&self, _sess: &Session) {}
-    fn print(&self, _req: &PrintRequest, _out: &mut dyn PrintBackendInfo, _sess: &Session) {}
+    fn print(&self, _req: &PrintRequest, _out: &mut String, _sess: &Session) {}
     fn target_features(&self, _sess: &Session, _allow_unstable: bool) -> Vec<Symbol> {
         vec![]
     }
@@ -150,19 +148,3 @@ pub trait ExtraBackendMethods:
         std::thread::Builder::new().name(name).spawn(f)
     }
 }
-
-pub trait PrintBackendInfo {
-    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>);
-}
-
-impl PrintBackendInfo for String {
-    fn infallible_write_fmt(&mut self, args: fmt::Arguments<'_>) {
-        fmt::Write::write_fmt(self, args).unwrap();
-    }
-}
-
-impl dyn PrintBackendInfo + '_ {
-    pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) {
-        self.infallible_write_fmt(args);
-    }
-}
diff --git a/compiler/rustc_codegen_ssa/src/traits/consts.rs b/compiler/rustc_codegen_ssa/src/traits/consts.rs
index 8cb17a5b37a..3da732602c5 100644
--- a/compiler/rustc_codegen_ssa/src/traits/consts.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/consts.rs
@@ -37,6 +37,5 @@ pub trait ConstMethods<'tcx>: BackendTypes {
 
     fn scalar_to_backend(&self, cv: Scalar, layout: abi::Scalar, llty: Self::Type) -> Self::Value;
 
-    fn const_bitcast(&self, val: Self::Value, ty: Self::Type) -> Self::Value;
     fn const_ptr_byte_offset(&self, val: Self::Value, offset: abi::Size) -> Self::Value;
 }
diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs
index 04e2b8796c4..0ace28ed3ba 100644
--- a/compiler/rustc_codegen_ssa/src/traits/misc.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs
@@ -9,7 +9,13 @@ pub trait MiscMethods<'tcx>: BackendTypes {
     fn vtables(
         &self,
     ) -> &RefCell<FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Self::Value>>;
-    fn check_overflow(&self) -> bool;
+    fn apply_vcall_visibility_metadata(
+        &self,
+        _ty: Ty<'tcx>,
+        _poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
+        _vtable: Self::Value,
+    ) {
+    }
     fn get_fn(&self, instance: Instance<'tcx>) -> Self::Function;
     fn get_fn_addr(&self, instance: Instance<'tcx>) -> Self::Value;
     fn eh_personality(&self) -> Self::Value;
diff --git a/compiler/rustc_codegen_ssa/src/traits/mod.rs b/compiler/rustc_codegen_ssa/src/traits/mod.rs
index 728c2bc8c49..8cb58bd4c70 100644
--- a/compiler/rustc_codegen_ssa/src/traits/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/mod.rs
@@ -30,9 +30,7 @@ mod write;
 
 pub use self::abi::AbiBuilderMethods;
 pub use self::asm::{AsmBuilderMethods, AsmMethods, GlobalAsmOperandRef, InlineAsmOperandRef};
-pub use self::backend::{
-    Backend, BackendTypes, CodegenBackend, ExtraBackendMethods, PrintBackendInfo,
-};
+pub use self::backend::{Backend, BackendTypes, CodegenBackend, ExtraBackendMethods};
 pub use self::builder::{BuilderMethods, OverflowOp};
 pub use self::consts::ConstMethods;
 pub use self::coverageinfo::CoverageInfoBuilderMethods;
diff --git a/compiler/rustc_codegen_ssa/src/traits/type_.rs b/compiler/rustc_codegen_ssa/src/traits/type_.rs
index 403f6a73277..b1bad6cfa6f 100644
--- a/compiler/rustc_codegen_ssa/src/traits/type_.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/type_.rs
@@ -12,7 +12,6 @@ use rustc_target::abi::{AddressSpace, Float, Integer};
 // This depends on `Backend` and not `BackendTypes`, because consumers will probably want to use
 // `LayoutOf` or `HasTyCtxt`. This way, they don't have to add a constraint on it themselves.
 pub trait BaseTypeMethods<'tcx>: Backend<'tcx> {
-    fn type_i1(&self) -> Self::Type;
     fn type_i8(&self) -> Self::Type;
     fn type_i16(&self) -> Self::Type;
     fn type_i32(&self) -> Self::Type;
@@ -27,7 +26,6 @@ pub trait BaseTypeMethods<'tcx>: Backend<'tcx> {
 
     fn type_array(&self, ty: Self::Type, len: u64) -> Self::Type;
     fn type_func(&self, args: &[Self::Type], ret: Self::Type) -> Self::Type;
-    fn type_struct(&self, els: &[Self::Type], packed: bool) -> Self::Type;
     fn type_kind(&self, ty: Self::Type) -> TypeKind;
     fn type_ptr(&self) -> Self::Type;
     fn type_ptr_ext(&self, address_space: AddressSpace) -> Self::Type;
@@ -115,8 +113,8 @@ pub trait LayoutTypeMethods<'tcx>: Backend<'tcx> {
     /// The backend type used for a rust type when it's in an SSA register.
     ///
     /// For nearly all types this is the same as the [`Self::backend_type`], however
-    /// `bool` (and other `0`-or-`1` values) are kept as [`BaseTypeMethods::type_i1`]
-    /// in registers but as [`BaseTypeMethods::type_i8`] in memory.
+    /// `bool` (and other `0`-or-`1` values) are kept as `i1` in registers but as
+    /// [`BaseTypeMethods::type_i8`] in memory.
     ///
     /// Converting values between the two different backend types is done using
     /// [`from_immediate`](super::BuilderMethods::from_immediate) and
diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs
index ab60cc37920..523d55fe2d0 100644
--- a/compiler/rustc_const_eval/src/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/check_consts/check.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
 use rustc_middle::ty::{Instance, InstanceKind, TypeVisitableExt};
 use rustc_mir_dataflow::Analysis;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, ObligationCtxt};
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitor};
 
@@ -135,6 +135,8 @@ impl<'mir, 'tcx> Qualifs<'mir, 'tcx> {
         ccx: &'mir ConstCx<'mir, 'tcx>,
         tainted_by_errors: Option<ErrorGuaranteed>,
     ) -> ConstQualifs {
+        // FIXME(explicit_tail_calls): uhhhh I think we can return without return now, does it change anything
+
         // Find the `Return` terminator if one exists.
         //
         // If no `Return` terminator exists, this MIR is divergent. Just return the conservative
@@ -711,7 +713,14 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
         self.super_terminator(terminator, location);
 
         match &terminator.kind {
-            TerminatorKind::Call { func, args, fn_span, call_source, .. } => {
+            TerminatorKind::Call { func, args, fn_span, .. }
+            | TerminatorKind::TailCall { func, args, fn_span, .. } => {
+                let call_source = match terminator.kind {
+                    TerminatorKind::Call { call_source, .. } => call_source,
+                    TerminatorKind::TailCall { .. } => CallSource::Normal,
+                    _ => unreachable!(),
+                };
+
                 let ConstCx { tcx, body, param_env, .. } = *self.ccx;
                 let caller = self.def_id();
 
@@ -768,7 +777,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                         is_trait = true;
 
                         if let Ok(Some(instance)) =
-                            Instance::resolve(tcx, param_env, callee, fn_args)
+                            Instance::try_resolve(tcx, param_env, callee, fn_args)
                             && let InstanceKind::Item(def) = instance.def
                         {
                             // Resolve a trait method call to its concrete implementation, which may be in a
@@ -783,7 +792,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                             callee,
                             args: fn_args,
                             span: *fn_span,
-                            call_source: *call_source,
+                            call_source,
                             feature: Some(if tcx.features().const_trait_impl {
                                 sym::effects
                             } else {
@@ -830,7 +839,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                         callee,
                         args: fn_args,
                         span: *fn_span,
-                        call_source: *call_source,
+                        call_source,
                         feature: None,
                     });
                     return;
diff --git a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
index f0763f1e490..f5e745454ab 100644
--- a/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
+++ b/compiler/rustc_const_eval/src/check_consts/post_drop_elaboration.rs
@@ -108,6 +108,7 @@ impl<'tcx> Visitor<'tcx> for CheckLiveDrops<'_, 'tcx> {
 
             mir::TerminatorKind::UnwindTerminate(_)
             | mir::TerminatorKind::Call { .. }
+            | mir::TerminatorKind::TailCall { .. }
             | mir::TerminatorKind::Assert { .. }
             | mir::TerminatorKind::FalseEdge { .. }
             | mir::TerminatorKind::FalseUnwind { .. }
diff --git a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
index 8c66888d100..7acd08e0cce 100644
--- a/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/fn_queries.rs
@@ -42,10 +42,7 @@ fn constness(tcx: TyCtxt<'_>, def_id: LocalDefId) -> hir::Constness {
         | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. }) => {
             hir::Constness::Const
         }
-        hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => tcx
-            .generics_of(def_id)
-            .host_effect_index
-            .map_or(hir::Constness::NotConst, |_| hir::Constness::Const),
+        hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.constness,
         hir::Node::ForeignItem(hir::ForeignItem { kind: hir::ForeignItemKind::Fn(..), .. }) => {
             // Intrinsics use `rustc_const_{un,}stable` attributes to indicate constness. All other
             // foreign items cannot be evaluated at compile-time.
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 99276bac035..17e1d8566c2 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -253,6 +253,7 @@ impl<'tcx> CompileTimeInterpCx<'tcx> {
                 ty::ParamEnv::reveal_all(),
                 const_def_id,
                 instance.args,
+                self.cur_span(),
             );
 
             return Ok(Some(new_instance));
diff --git a/compiler/rustc_const_eval/src/interpret/cast.rs b/compiler/rustc_const_eval/src/interpret/cast.rs
index a13630ce084..83b61ab1749 100644
--- a/compiler/rustc_const_eval/src/interpret/cast.rs
+++ b/compiler/rustc_const_eval/src/interpret/cast.rs
@@ -1,6 +1,6 @@
 use std::assert_matches::assert_matches;
 
-use rustc_apfloat::ieee::{Double, Single};
+use rustc_apfloat::ieee::{Double, Half, Quad, Single};
 use rustc_apfloat::{Float, FloatConvert};
 use rustc_middle::mir::interpret::{InterpResult, PointerArithmetic, Scalar};
 use rustc_middle::mir::CastKind;
@@ -187,10 +187,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             bug!("FloatToFloat/FloatToInt cast: source type {} is not a float type", src.layout.ty)
         };
         let val = match fty {
-            FloatTy::F16 => unimplemented!("f16_f128"),
+            FloatTy::F16 => self.cast_from_float(src.to_scalar().to_f16()?, cast_to.ty),
             FloatTy::F32 => self.cast_from_float(src.to_scalar().to_f32()?, cast_to.ty),
             FloatTy::F64 => self.cast_from_float(src.to_scalar().to_f64()?, cast_to.ty),
-            FloatTy::F128 => unimplemented!("f16_f128"),
+            FloatTy::F128 => self.cast_from_float(src.to_scalar().to_f128()?, cast_to.ty),
         };
         Ok(ImmTy::from_scalar(val, cast_to))
     }
@@ -296,18 +296,18 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             Float(fty) if signed => {
                 let v = v as i128;
                 match fty {
-                    FloatTy::F16 => unimplemented!("f16_f128"),
+                    FloatTy::F16 => Scalar::from_f16(Half::from_i128(v).value),
                     FloatTy::F32 => Scalar::from_f32(Single::from_i128(v).value),
                     FloatTy::F64 => Scalar::from_f64(Double::from_i128(v).value),
-                    FloatTy::F128 => unimplemented!("f16_f128"),
+                    FloatTy::F128 => Scalar::from_f128(Quad::from_i128(v).value),
                 }
             }
             // unsigned int -> float
             Float(fty) => match fty {
-                FloatTy::F16 => unimplemented!("f16_f128"),
+                FloatTy::F16 => Scalar::from_f16(Half::from_u128(v).value),
                 FloatTy::F32 => Scalar::from_f32(Single::from_u128(v).value),
                 FloatTy::F64 => Scalar::from_f64(Double::from_u128(v).value),
-                FloatTy::F128 => unimplemented!("f16_f128"),
+                FloatTy::F128 => Scalar::from_f128(Quad::from_u128(v).value),
             },
 
             // u8 -> char
@@ -321,7 +321,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Low-level cast helper function. Converts an apfloat `f` into int or float types.
     fn cast_from_float<F>(&self, f: F, dest_ty: Ty<'tcx>) -> Scalar<M::Provenance>
     where
-        F: Float + Into<Scalar<M::Provenance>> + FloatConvert<Single> + FloatConvert<Double>,
+        F: Float
+            + Into<Scalar<M::Provenance>>
+            + FloatConvert<Half>
+            + FloatConvert<Single>
+            + FloatConvert<Double>
+            + FloatConvert<Quad>,
     {
         use rustc_type_ir::TyKind::*;
 
@@ -358,10 +363,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             }
             // float -> float
             Float(fty) => match fty {
-                FloatTy::F16 => unimplemented!("f16_f128"),
+                FloatTy::F16 => Scalar::from_f16(adjust_nan(self, f, f.convert(&mut false).value)),
                 FloatTy::F32 => Scalar::from_f32(adjust_nan(self, f, f.convert(&mut false).value)),
                 FloatTy::F64 => Scalar::from_f64(adjust_nan(self, f, f.convert(&mut false).value)),
-                FloatTy::F128 => unimplemented!("f16_f128"),
+                FloatTy::F128 => {
+                    Scalar::from_f128(adjust_nan(self, f, f.convert(&mut false).value))
+                }
             },
             // That's it.
             _ => span_bug!(self.cur_span(), "invalid float to {} cast", dest_ty),
diff --git a/compiler/rustc_const_eval/src/interpret/discriminant.rs b/compiler/rustc_const_eval/src/interpret/discriminant.rs
index b3a139d553a..181c7115386 100644
--- a/compiler/rustc_const_eval/src/interpret/discriminant.rs
+++ b/compiler/rustc_const_eval/src/interpret/discriminant.rs
@@ -245,7 +245,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 // The tag of a `Single` enum is like the tag of the niched
                 // variant: there's no tag as the discriminant is encoded
                 // entirely implicitly. If `write_discriminant` ever hits this
-                // case, we do a "validation read" to ensure the the right
+                // case, we do a "validation read" to ensure the right
                 // discriminant is encoded implicitly, so any attempt to write
                 // the wrong discriminant for a `Single` enum will reliably
                 // result in UB.
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 67eeb1b3b87..6d3e5ea1031 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -26,8 +26,8 @@ use rustc_target::abi::{call::FnAbi, Align, HasDataLayout, Size, TargetDataLayou
 use super::{
     err_inval, throw_inval, throw_ub, throw_ub_custom, throw_unsup, GlobalId, Immediate,
     InterpErrorInfo, InterpResult, MPlaceTy, Machine, MemPlace, MemPlaceMeta, Memory, MemoryKind,
-    OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance, Scalar,
-    StackPopJump,
+    OpTy, Operand, Place, PlaceTy, Pointer, PointerArithmetic, Projectable, Provenance,
+    ReturnAction, Scalar,
 };
 use crate::errors;
 use crate::util;
@@ -159,6 +159,19 @@ pub enum StackPopCleanup {
     Root { cleanup: bool },
 }
 
+/// Return type of [`InterpCx::pop_stack_frame`].
+pub struct StackPopInfo<'tcx, Prov: Provenance> {
+    /// Additional information about the action to be performed when returning from the popped
+    /// stack frame.
+    pub return_action: ReturnAction,
+
+    /// [`return_to_block`](Frame::return_to_block) of the popped stack frame.
+    pub return_to_block: StackPopCleanup,
+
+    /// [`return_place`](Frame::return_place) of the popped stack frame.
+    pub return_place: MPlaceTy<'tcx, Prov>,
+}
+
 /// State of a local variable including a memoized layout
 #[derive(Clone)]
 pub struct LocalState<'tcx, Prov: Provenance = CtfeProvenance> {
@@ -618,7 +631,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         trace!("resolve: {:?}, {:#?}", def, args);
         trace!("param_env: {:#?}", self.param_env);
         trace!("args: {:#?}", args);
-        match ty::Instance::resolve(*self.tcx, self.param_env, def, args) {
+        match ty::Instance::try_resolve(*self.tcx, self.param_env, def, args) {
             Ok(Some(instance)) => Ok(instance),
             Ok(None) => throw_inval!(TooGeneric),
 
@@ -803,14 +816,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         return_to_block: StackPopCleanup,
     ) -> InterpResult<'tcx> {
         trace!("body: {:#?}", body);
+
+        // First push a stack frame so we have access to the local args
+        self.push_new_stack_frame(instance, body, return_to_block, return_place.clone())?;
+
+        self.after_stack_frame_push(instance, body)?;
+
+        Ok(())
+    }
+
+    /// Creates a new stack frame, initializes it and pushes it onto the stack.
+    /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
+    fn push_new_stack_frame(
+        &mut self,
+        instance: ty::Instance<'tcx>,
+        body: &'tcx mir::Body<'tcx>,
+        return_to_block: StackPopCleanup,
+        return_place: MPlaceTy<'tcx, M::Provenance>,
+    ) -> InterpResult<'tcx> {
         let dead_local = LocalState { value: LocalValue::Dead, layout: Cell::new(None) };
         let locals = IndexVec::from_elem(dead_local, &body.local_decls);
-        // First push a stack frame so we have access to the local args
         let pre_frame = Frame {
             body,
             loc: Right(body.span), // Span used for errors caused during preamble.
             return_to_block,
-            return_place: return_place.clone(),
+            return_place,
             locals,
             instance,
             tracing_span: SpanGuard::new(),
@@ -819,6 +849,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let frame = M::init_frame(self, pre_frame)?;
         self.stack_mut().push(frame);
 
+        Ok(())
+    }
+
+    /// A private helper for [`push_stack_frame`](InterpCx::push_stack_frame).
+    fn after_stack_frame_push(
+        &mut self,
+        instance: ty::Instance<'tcx>,
+        body: &'tcx mir::Body<'tcx>,
+    ) -> InterpResult<'tcx> {
         // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
         for &const_ in &body.required_consts {
             let c =
@@ -839,6 +878,61 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         Ok(())
     }
 
+    /// Pops a stack frame from the stack and returns some information about it.
+    ///
+    /// This also deallocates locals, if necessary.
+    ///
+    /// [`M::before_stack_pop`] should be called before calling this function.
+    /// [`M::after_stack_pop`] is called by this function automatically.
+    ///
+    /// [`M::before_stack_pop`]: Machine::before_stack_pop
+    /// [`M::after_stack_pop`]: Machine::after_stack_pop
+    pub fn pop_stack_frame(
+        &mut self,
+        unwinding: bool,
+    ) -> InterpResult<'tcx, StackPopInfo<'tcx, M::Provenance>> {
+        let cleanup = self.cleanup_current_frame_locals()?;
+
+        let frame =
+            self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
+
+        let return_to_block = frame.return_to_block;
+        let return_place = frame.return_place.clone();
+
+        let return_action;
+        if cleanup {
+            return_action = M::after_stack_pop(self, frame, unwinding)?;
+            assert_ne!(return_action, ReturnAction::NoCleanup);
+        } else {
+            return_action = ReturnAction::NoCleanup;
+        };
+
+        Ok(StackPopInfo { return_action, return_to_block, return_place })
+    }
+
+    /// A private helper for [`pop_stack_frame`](InterpCx::pop_stack_frame).
+    /// Returns `true` if cleanup has been done, `false` otherwise.
+    fn cleanup_current_frame_locals(&mut self) -> InterpResult<'tcx, bool> {
+        // Cleanup: deallocate locals.
+        // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
+        // We do this while the frame is still on the stack, so errors point to the callee.
+        let return_to_block = self.frame().return_to_block;
+        let cleanup = match return_to_block {
+            StackPopCleanup::Goto { .. } => true,
+            StackPopCleanup::Root { cleanup, .. } => cleanup,
+        };
+
+        if cleanup {
+            // We need to take the locals out, since we need to mutate while iterating.
+            let locals = mem::take(&mut self.frame_mut().locals);
+            for local in &locals {
+                self.deallocate_local(local.value)?;
+            }
+        }
+
+        Ok(cleanup)
+    }
+
     /// Jump to the given block.
     #[inline]
     pub fn go_to_block(&mut self, target: mir::BasicBlock) {
@@ -886,7 +980,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     }
 
     /// Pops the current frame from the stack, deallocating the
-    /// memory for allocated locals.
+    /// memory for allocated locals, and jumps to an appropriate place.
     ///
     /// If `unwinding` is `false`, then we are performing a normal return
     /// from a function. In this case, we jump back into the frame of the caller,
@@ -899,7 +993,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// The cleanup block ends with a special `Resume` terminator, which will
     /// cause us to continue unwinding.
     #[instrument(skip(self), level = "debug")]
-    pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
+    pub(super) fn return_from_current_stack_frame(
+        &mut self,
+        unwinding: bool,
+    ) -> InterpResult<'tcx> {
         info!(
             "popping stack frame ({})",
             if unwinding { "during unwinding" } else { "returning from function" }
@@ -947,45 +1044,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             Ok(())
         };
 
-        // Cleanup: deallocate locals.
-        // Usually we want to clean up (deallocate locals), but in a few rare cases we don't.
-        // We do this while the frame is still on the stack, so errors point to the callee.
-        let return_to_block = self.frame().return_to_block;
-        let cleanup = match return_to_block {
-            StackPopCleanup::Goto { .. } => true,
-            StackPopCleanup::Root { cleanup, .. } => cleanup,
-        };
-        if cleanup {
-            // We need to take the locals out, since we need to mutate while iterating.
-            let locals = mem::take(&mut self.frame_mut().locals);
-            for local in &locals {
-                self.deallocate_local(local.value)?;
-            }
-        }
-
         // All right, now it is time to actually pop the frame.
-        // Note that its locals are gone already, but that's fine.
-        let frame =
-            self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
+        let stack_pop_info = self.pop_stack_frame(unwinding)?;
+
         // Report error from return value copy, if any.
         copy_ret_result?;
 
-        // If we are not doing cleanup, also skip everything else.
-        if !cleanup {
-            assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
-            assert!(!unwinding, "tried to skip cleanup during unwinding");
-            // Skip machine hook.
-            return Ok(());
-        }
-        if M::after_stack_pop(self, frame, unwinding)? == StackPopJump::NoJump {
-            // The hook already did everything.
-            return Ok(());
+        match stack_pop_info.return_action {
+            ReturnAction::Normal => {}
+            ReturnAction::NoJump => {
+                // The hook already did everything.
+                return Ok(());
+            }
+            ReturnAction::NoCleanup => {
+                // If we are not doing cleanup, also skip everything else.
+                assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
+                assert!(!unwinding, "tried to skip cleanup during unwinding");
+                // Skip machine hook.
+                return Ok(());
+            }
         }
 
         // Normal return, figure out where to jump.
         if unwinding {
             // Follow the unwind edge.
-            let unwind = match return_to_block {
+            let unwind = match stack_pop_info.return_to_block {
                 StackPopCleanup::Goto { unwind, .. } => unwind,
                 StackPopCleanup::Root { .. } => {
                     panic!("encountered StackPopCleanup::Root when unwinding!")
@@ -995,7 +1078,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             self.unwind_to_block(unwind)
         } else {
             // Follow the normal return edge.
-            match return_to_block {
+            match stack_pop_info.return_to_block {
                 StackPopCleanup::Goto { ret, .. } => self.return_to_block(ret),
                 StackPopCleanup::Root { .. } => {
                     assert!(
diff --git a/compiler/rustc_const_eval/src/interpret/intrinsics.rs b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
index 1d54da267ee..d86f1a7a34f 100644
--- a/compiler/rustc_const_eval/src/interpret/intrinsics.rs
+++ b/compiler/rustc_const_eval/src/interpret/intrinsics.rs
@@ -301,9 +301,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         }
                         // The signed form of the intrinsic allows this. If we interpret the
                         // difference as isize, we'll get the proper signed difference. If that
-                        // seems *positive*, they were more than isize::MAX apart.
+                        // seems *positive* or equal to isize::MIN, they were more than isize::MAX apart.
                         let dist = val.to_target_isize(self)?;
-                        if dist >= 0 {
+                        if dist >= 0 || i128::from(dist) == self.pointer_size().signed_int_min() {
                             throw_ub_custom!(
                                 fluent::const_eval_offset_from_underflow,
                                 name = intrinsic_name,
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index e91ab7c1791..7f2e9ce06a5 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -23,10 +23,11 @@ use super::{
     MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance,
 };
 
-/// Data returned by Machine::stack_pop,
-/// to provide further control over the popping of the stack frame
+/// Data returned by [`Machine::after_stack_pop`], and consumed by
+/// [`InterpCx::return_from_current_stack_frame`] to determine what actions should be done when
+/// returning from a stack frame.
 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
-pub enum StackPopJump {
+pub enum ReturnAction {
     /// Indicates that no special handling should be
     /// done - we'll either return normally or unwind
     /// based on the terminator for the function
@@ -36,6 +37,9 @@ pub enum StackPopJump {
     /// Indicates that we should *not* jump to the return/unwind address, as the callback already
     /// took care of everything.
     NoJump,
+
+    /// Returned by [`InterpCx::pop_stack_frame`] when no cleanup should be done.
+    NoCleanup,
 }
 
 /// Whether this kind of memory is allowed to leak
@@ -522,10 +526,10 @@ pub trait Machine<'tcx>: Sized {
         _ecx: &mut InterpCx<'tcx, Self>,
         _frame: Frame<'tcx, Self::Provenance, Self::FrameExtra>,
         unwinding: bool,
-    ) -> InterpResult<'tcx, StackPopJump> {
+    ) -> InterpResult<'tcx, ReturnAction> {
         // By default, we do not support unwinding from panics
         assert!(!unwinding);
-        Ok(StackPopJump::Normal)
+        Ok(ReturnAction::Normal)
     }
 
     /// Called immediately after actual memory was allocated for a local
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 9d0c4908225..36fe8dfdd29 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -308,7 +308,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         let Some((alloc_kind, mut alloc)) = self.memory.alloc_map.remove(&alloc_id) else {
             // Deallocating global memory -- always an error
             return Err(match self.tcx.try_get_global_alloc(alloc_id) {
-                Some(GlobalAlloc::Function(..)) => {
+                Some(GlobalAlloc::Function { .. }) => {
                     err_ub_custom!(
                         fluent::const_eval_invalid_dealloc,
                         alloc_id = alloc_id,
@@ -555,7 +555,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 // Memory of a constant or promoted or anonymous memory referenced by a static.
                 (mem, None)
             }
-            Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
+            Some(GlobalAlloc::Function { .. }) => throw_ub!(DerefFunctionPointer(id)),
             Some(GlobalAlloc::VTable(..)) => throw_ub!(DerefVTablePointer(id)),
             None => throw_ub!(PointerUseAfterFree(id, CheckInAllocMsg::MemoryAccessTest)),
             Some(GlobalAlloc::Static(def_id)) => {
@@ -828,7 +828,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 let alloc = alloc.inner();
                 (alloc.size(), alloc.align, AllocKind::LiveData)
             }
-            Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
+            Some(GlobalAlloc::Function { .. }) => {
+                bug!("We already checked function pointers above")
+            }
             Some(GlobalAlloc::VTable(..)) => {
                 // No data to be accessed here. But vtables are pointer-aligned.
                 return (Size::ZERO, self.tcx.data_layout.pointer_align.abi, AllocKind::VTable);
@@ -865,7 +867,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             Some(FnVal::Other(*extra))
         } else {
             match self.tcx.try_get_global_alloc(id) {
-                Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)),
+                Some(GlobalAlloc::Function { instance, .. }) => Some(FnVal::Instance(instance)),
                 _ => None,
             }
         }
@@ -1056,8 +1058,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> std::fmt::Debug for DumpAllocs<'a, 'tcx, M> {
                                 alloc.inner(),
                             )?;
                         }
-                        Some(GlobalAlloc::Function(func)) => {
-                            write!(fmt, " (fn: {func})")?;
+                        Some(GlobalAlloc::Function { instance, .. }) => {
+                            write!(fmt, " (fn: {instance})")?;
                         }
                         Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
                             write!(fmt, " (vtable: impl {trait_ref} for {ty})")?;
diff --git a/compiler/rustc_const_eval/src/interpret/mod.rs b/compiler/rustc_const_eval/src/interpret/mod.rs
index 7d7b421f869..f703c6fbe3e 100644
--- a/compiler/rustc_const_eval/src/interpret/mod.rs
+++ b/compiler/rustc_const_eval/src/interpret/mod.rs
@@ -26,7 +26,7 @@ pub use self::intern::{
     intern_const_alloc_for_constprop, intern_const_alloc_recursive, HasStaticRootDefId, InternKind,
     InternResult,
 };
-pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
+pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, ReturnAction};
 pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
 pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
 pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs
index 1baf62baa81..b3124dfdfbc 100644
--- a/compiler/rustc_const_eval/src/interpret/step.rs
+++ b/compiler/rustc_const_eval/src/interpret/step.rs
@@ -32,7 +32,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             // We are unwinding and this fn has no cleanup code.
             // Just go on unwinding.
             trace!("unwinding: skipping frame");
-            self.pop_stack_frame(/* unwinding */ true)?;
+            self.return_from_current_stack_frame(/* unwinding */ true)?;
             return Ok(true);
         };
         let basic_block = &self.body().basic_blocks[loc.block];
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 74521d0f493..25f6bd64055 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -4,9 +4,8 @@ use either::Either;
 use rustc_middle::ty::TyCtxt;
 use tracing::trace;
 
-use rustc_middle::span_bug;
 use rustc_middle::{
-    mir,
+    bug, mir, span_bug,
     ty::{
         self,
         layout::{FnAbiOf, IntegerExt, LayoutOf, TyAndLayout},
@@ -26,7 +25,10 @@ use super::{
     InterpResult, MPlaceTy, Machine, OpTy, PlaceTy, Projectable, Provenance, Scalar,
     StackPopCleanup,
 };
-use crate::fluent_generated as fluent;
+use crate::{
+    fluent_generated as fluent,
+    interpret::{eval_context::StackPopInfo, ReturnAction},
+};
 
 /// An argment passed to a function.
 #[derive(Clone, Debug)]
@@ -47,6 +49,15 @@ impl<'tcx, Prov: Provenance> FnArg<'tcx, Prov> {
     }
 }
 
+struct EvaluatedCalleeAndArgs<'tcx, M: Machine<'tcx>> {
+    callee: FnVal<'tcx, M::ExtraFnVal>,
+    args: Vec<FnArg<'tcx, M::Provenance>>,
+    fn_sig: ty::FnSig<'tcx>,
+    fn_abi: &'tcx FnAbi<'tcx, Ty<'tcx>>,
+    /// True if the function is marked as `#[track_caller]` ([`ty::InstanceKind::requires_caller_location`])
+    with_caller_location: bool,
+}
+
 impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
     /// Make a copy of the given fn_arg. Any `InPlace` are degenerated to copies, no protection of the
     /// original memory occurs.
@@ -84,7 +95,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         use rustc_middle::mir::TerminatorKind::*;
         match terminator.kind {
             Return => {
-                self.pop_stack_frame(/* unwinding */ false)?
+                self.return_from_current_stack_frame(/* unwinding */ false)?
             }
 
             Goto { target } => self.go_to_block(target),
@@ -124,40 +135,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
             } => {
                 let old_stack = self.frame_idx();
                 let old_loc = self.frame().loc;
-                let func = self.eval_operand(func, None)?;
-                let args = self.eval_fn_call_arguments(args)?;
-
-                let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
-                let fn_sig =
-                    self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
-                let extra_args = &args[fn_sig.inputs().len()..];
-                let extra_args =
-                    self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
-
-                let (fn_val, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
-                    ty::FnPtr(_sig) => {
-                        let fn_ptr = self.read_pointer(&func)?;
-                        let fn_val = self.get_ptr_fn(fn_ptr)?;
-                        (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
-                    }
-                    ty::FnDef(def_id, args) => {
-                        let instance = self.resolve(def_id, args)?;
-                        (
-                            FnVal::Instance(instance),
-                            self.fn_abi_of_instance(instance, extra_args)?,
-                            instance.def.requires_caller_location(*self.tcx),
-                        )
-                    }
-                    _ => span_bug!(
-                        terminator.source_info.span,
-                        "invalid callee of type {}",
-                        func.layout.ty
-                    ),
-                };
+
+                let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
+                    self.eval_callee_and_args(terminator, func, args)?;
 
                 let destination = self.force_allocation(&self.eval_place(destination)?)?;
                 self.eval_fn_call(
-                    fn_val,
+                    callee,
                     (fn_sig.abi, fn_abi),
                     &args,
                     with_caller_location,
@@ -172,6 +156,22 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 }
             }
 
+            TailCall { ref func, ref args, fn_span: _ } => {
+                let old_frame_idx = self.frame_idx();
+
+                let EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location } =
+                    self.eval_callee_and_args(terminator, func, args)?;
+
+                self.eval_fn_tail_call(callee, (fn_sig.abi, fn_abi), &args, with_caller_location)?;
+
+                if self.frame_idx() != old_frame_idx {
+                    span_bug!(
+                        terminator.source_info.span,
+                        "evaluating this tail call pushed a new stack frame"
+                    );
+                }
+            }
+
             Drop { place, target, unwind, replace: _ } => {
                 let place = self.eval_place(place)?;
                 let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty);
@@ -209,7 +209,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                 trace!("unwinding: resuming from cleanup");
                 // By definition, a Resume terminator means
                 // that we're unwinding
-                self.pop_stack_frame(/* unwinding */ true)?;
+                self.return_from_current_stack_frame(/* unwinding */ true)?;
                 return Ok(());
             }
 
@@ -514,6 +514,45 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         Ok(())
     }
 
+    /// Shared part of `Call` and `TailCall` implementation — finding and evaluating all the
+    /// necessary information about callee and arguments to make a call.
+    fn eval_callee_and_args(
+        &self,
+        terminator: &mir::Terminator<'tcx>,
+        func: &mir::Operand<'tcx>,
+        args: &[Spanned<mir::Operand<'tcx>>],
+    ) -> InterpResult<'tcx, EvaluatedCalleeAndArgs<'tcx, M>> {
+        let func = self.eval_operand(func, None)?;
+        let args = self.eval_fn_call_arguments(args)?;
+
+        let fn_sig_binder = func.layout.ty.fn_sig(*self.tcx);
+        let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig_binder);
+        let extra_args = &args[fn_sig.inputs().len()..];
+        let extra_args =
+            self.tcx.mk_type_list_from_iter(extra_args.iter().map(|arg| arg.layout().ty));
+
+        let (callee, fn_abi, with_caller_location) = match *func.layout.ty.kind() {
+            ty::FnPtr(_sig) => {
+                let fn_ptr = self.read_pointer(&func)?;
+                let fn_val = self.get_ptr_fn(fn_ptr)?;
+                (fn_val, self.fn_abi_of_fn_ptr(fn_sig_binder, extra_args)?, false)
+            }
+            ty::FnDef(def_id, args) => {
+                let instance = self.resolve(def_id, args)?;
+                (
+                    FnVal::Instance(instance),
+                    self.fn_abi_of_instance(instance, extra_args)?,
+                    instance.def.requires_caller_location(*self.tcx),
+                )
+            }
+            _ => {
+                span_bug!(terminator.source_info.span, "invalid callee of type {}", func.layout.ty)
+            }
+        };
+
+        Ok(EvaluatedCalleeAndArgs { callee, args, fn_sig, fn_abi, with_caller_location })
+    }
+
     /// Call this function -- pushing the stack frame and initializing the arguments.
     ///
     /// `caller_fn_abi` is used to determine if all the arguments are passed the proper way.
@@ -883,13 +922,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
                         ty::ExistentialTraitRef::erase_self_ty(tcx, virtual_trait_ref);
                     let concrete_trait_ref = existential_trait_ref.with_self_ty(tcx, dyn_ty);
 
-                    let concrete_method = Instance::resolve_for_vtable(
+                    let concrete_method = Instance::expect_resolve_for_vtable(
                         tcx,
                         self.param_env,
                         def_id,
                         instance.args.rebase_onto(tcx, trait_def_id, concrete_trait_ref.args),
-                    )
-                    .unwrap();
+                        self.cur_span(),
+                    );
                     assert_eq!(fn_inst, concrete_method);
                 }
 
@@ -924,6 +963,49 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         }
     }
 
+    pub(crate) fn eval_fn_tail_call(
+        &mut self,
+        fn_val: FnVal<'tcx, M::ExtraFnVal>,
+        (caller_abi, caller_fn_abi): (Abi, &FnAbi<'tcx, Ty<'tcx>>),
+        args: &[FnArg<'tcx, M::Provenance>],
+        with_caller_location: bool,
+    ) -> InterpResult<'tcx> {
+        trace!("eval_fn_call: {:#?}", fn_val);
+
+        // This is the "canonical" implementation of tails calls,
+        // a pop of the current stack frame, followed by a normal call
+        // which pushes a new stack frame, with the return address from
+        // the popped stack frame.
+        //
+        // Note that we are using `pop_stack_frame` and not `return_from_current_stack_frame`,
+        // as the latter "executes" the goto to the return block, but we don't want to,
+        // only the tail called function should return to the current return block.
+        M::before_stack_pop(self, self.frame())?;
+
+        let StackPopInfo { return_action, return_to_block, return_place } =
+            self.pop_stack_frame(false)?;
+
+        assert_eq!(return_action, ReturnAction::Normal);
+
+        let StackPopCleanup::Goto { ret, unwind } = return_to_block else {
+            bug!("can't tailcall as root");
+        };
+
+        // FIXME(explicit_tail_calls):
+        //   we should check if both caller&callee can/n't unwind,
+        //   see <https://github.com/rust-lang/rust/pull/113128#issuecomment-1614979803>
+
+        self.eval_fn_call(
+            fn_val,
+            (caller_abi, caller_fn_abi),
+            args,
+            with_caller_location,
+            &return_place,
+            ret,
+            unwind,
+        )
+    }
+
     fn check_fn_target_features(&self, instance: ty::Instance<'tcx>) -> InterpResult<'tcx, ()> {
         // Calling functions with `#[target_feature]` is not unsafe on WASM, see #84988
         let attrs = self.tcx.codegen_fn_attrs(instance.def_id());
diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs
index add48e1b186..7fea0617666 100644
--- a/compiler/rustc_const_eval/src/interpret/validity.rs
+++ b/compiler/rustc_const_eval/src/interpret/validity.rs
@@ -745,7 +745,7 @@ fn mutability<'tcx>(ecx: &InterpCx<'tcx, impl Machine<'tcx>>, alloc_id: AllocId)
             }
         }
         GlobalAlloc::Memory(alloc) => alloc.inner().mutability,
-        GlobalAlloc::Function(..) | GlobalAlloc::VTable(..) => {
+        GlobalAlloc::Function { .. } | GlobalAlloc::VTable(..) => {
             // These are immutable, we better don't allow mutable pointers here.
             Mutability::Not
         }
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index f525510030b..c4b2e067bbe 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -50,7 +50,7 @@ libc = "0.2"
 memmap2 = "0.2.1"
 # tidy-alphabetical-end
 
-[target.'cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))'.dependencies]
+[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
 portable-atomic = "1.5.1"
 
 [features]
diff --git a/compiler/rustc_data_structures/src/marker.rs b/compiler/rustc_data_structures/src/marker.rs
index 32fad0de1aa..83fdaff515b 100644
--- a/compiler/rustc_data_structures/src/marker.rs
+++ b/compiler/rustc_data_structures/src/marker.rs
@@ -147,14 +147,13 @@ cfg_match! {
             [crate::owned_slice::OwnedSlice]
         );
 
-        // MIPS, PowerPC and SPARC platforms with 32-bit pointers do not
-        // have AtomicU64 type.
-        #[cfg(not(any(target_arch = "powerpc", target_arch = "powerpc", target_arch = "sparc")))]
+        // Use portable AtomicU64 for targets without native 64-bit atomics
+        #[cfg(target_has_atomic = "64")]
         already_sync!(
             [std::sync::atomic::AtomicU64]
         );
 
-        #[cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))]
+        #[cfg(not(target_has_atomic = "64"))]
         already_sync!(
             [portable_atomic::AtomicU64]
         );
diff --git a/compiler/rustc_data_structures/src/sip128.rs b/compiler/rustc_data_structures/src/sip128.rs
index fed23df10dc..812ed410a94 100644
--- a/compiler/rustc_data_structures/src/sip128.rs
+++ b/compiler/rustc_data_structures/src/sip128.rs
@@ -70,18 +70,19 @@ macro_rules! compress {
     ($state:expr) => {{ compress!($state.v0, $state.v1, $state.v2, $state.v3) }};
     ($v0:expr, $v1:expr, $v2:expr, $v3:expr) => {{
         $v0 = $v0.wrapping_add($v1);
+        $v2 = $v2.wrapping_add($v3);
         $v1 = $v1.rotate_left(13);
         $v1 ^= $v0;
-        $v0 = $v0.rotate_left(32);
-        $v2 = $v2.wrapping_add($v3);
         $v3 = $v3.rotate_left(16);
         $v3 ^= $v2;
-        $v0 = $v0.wrapping_add($v3);
-        $v3 = $v3.rotate_left(21);
-        $v3 ^= $v0;
+        $v0 = $v0.rotate_left(32);
+
         $v2 = $v2.wrapping_add($v1);
+        $v0 = $v0.wrapping_add($v3);
         $v1 = $v1.rotate_left(17);
         $v1 ^= $v2;
+        $v3 = $v3.rotate_left(21);
+        $v3 ^= $v0;
         $v2 = $v2.rotate_left(32);
     }};
 }
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index 5ae79ca988f..058a675c40d 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -35,11 +35,11 @@
 //! |                         |                     |                                 |
 //! | `ParallelIterator`      | `Iterator`          | `rayon::iter::ParallelIterator` |
 //!
-//! [^1] `MTLock` is similar to `Lock`, but the serial version avoids the cost
+//! [^1]: `MTLock` is similar to `Lock`, but the serial version avoids the cost
 //! of a `RefCell`. This is appropriate when interior mutability is not
 //! required.
 //!
-//! [^2] `MTRef`, `MTLockRef` are type aliases.
+//! [^2]: `MTRef`, `MTLockRef` are type aliases.
 
 pub use crate::marker::*;
 use std::collections::HashMap;
@@ -270,12 +270,11 @@ cfg_match! {
 
         pub use std::sync::atomic::{AtomicBool, AtomicUsize, AtomicU32};
 
-        // MIPS, PowerPC and SPARC platforms with 32-bit pointers do not
-        // have AtomicU64 type.
-        #[cfg(not(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc")))]
+        // Use portable AtomicU64 for targets without native 64-bit atomics
+        #[cfg(target_has_atomic = "64")]
         pub use std::sync::atomic::AtomicU64;
 
-        #[cfg(any(target_arch = "mips", target_arch = "powerpc", target_arch = "sparc"))]
+        #[cfg(not(target_has_atomic = "64"))]
         pub use portable_atomic::AtomicU64;
 
         pub use std::sync::Arc as Lrc;
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index bbe9741bf44..ad2acb03b3f 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -30,7 +30,7 @@ use rustc_errors::{
 };
 use rustc_feature::find_gated_cfg;
 use rustc_interface::util::{self, get_codegen_backend};
-use rustc_interface::{interface, passes, Queries};
+use rustc_interface::{interface, passes, Linker, Queries};
 use rustc_lint::unerased_lint_store;
 use rustc_metadata::creader::MetadataLoader;
 use rustc_metadata::locator;
@@ -41,7 +41,6 @@ use rustc_session::getopts::{self, Matches};
 use rustc_session::lint::{Lint, LintId};
 use rustc_session::output::collect_crate_types;
 use rustc_session::{config, filesearch, EarlyDiagCtxt, Session};
-use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::source_map::FileLoader;
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
@@ -448,21 +447,9 @@ fn run_compiler(
                 return early_exit();
             }
 
-            let linker = queries.codegen_and_build_linker()?;
-
-            // This must run after monomorphization so that all generic types
-            // have been instantiated.
-            if sess.opts.unstable_opts.print_type_sizes {
-                sess.code_stats.print_type_sizes();
-            }
-
-            if sess.opts.unstable_opts.print_vtable_sizes {
-                let crate_name = queries.global_ctxt()?.enter(|tcx| tcx.crate_name(LOCAL_CRATE));
-
-                sess.code_stats.print_vtable_sizes(crate_name);
-            }
-
-            Ok(Some(linker))
+            queries.global_ctxt()?.enter(|tcx| {
+                Ok(Some(Linker::codegen_and_build_linker(tcx, &*compiler.codegen_backend)?))
+            })
         })?;
 
         // Linking is done outside the `compiler.enter()` so that the
diff --git a/compiler/rustc_error_codes/src/error_codes/E0502.md b/compiler/rustc_error_codes/src/error_codes/E0502.md
index dc3ffdfddd9..85f38b9286f 100644
--- a/compiler/rustc_error_codes/src/error_codes/E0502.md
+++ b/compiler/rustc_error_codes/src/error_codes/E0502.md
@@ -1,4 +1,5 @@
-A variable already borrowed as immutable was borrowed as mutable.
+A variable already borrowed with a certain mutability (either mutable or
+immutable) was borrowed again with a different mutability.
 
 Erroneous code example:
 
@@ -13,7 +14,7 @@ fn foo(a: &mut i32) {
 ```
 
 To fix this error, ensure that you don't have any other references to the
-variable before trying to access it mutably:
+variable before trying to access it with a different mutability:
 
 ```
 fn bar(x: &mut i32) {}
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index e580910af77..d500f6d88a0 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -510,7 +510,7 @@ pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> {
 // would be bad.
 impl<G> !Clone for Diag<'_, G> {}
 
-rustc_data_structures::static_assert_size!(Diag<'_, ()>, 2 * std::mem::size_of::<usize>());
+rustc_data_structures::static_assert_size!(Diag<'_, ()>, 3 * std::mem::size_of::<usize>());
 
 impl<G: EmissionGuarantee> Deref for Diag<'_, G> {
     type Target = DiagInner;
@@ -582,6 +582,11 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
         Self::new_diagnostic(dcx, DiagInner::new(level, message))
     }
 
+    /// Allow moving diagnostics between different error tainting contexts
+    pub fn with_dcx(mut self, dcx: DiagCtxtHandle<'_>) -> Diag<'_, G> {
+        Diag { dcx, diag: self.diag.take(), _marker: PhantomData }
+    }
+
     /// Creates a new `Diag` with an already constructed diagnostic.
     #[track_caller]
     pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self {
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 0af80bc5c67..e6ca1bf7bc4 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -298,15 +298,21 @@ impl IntoDiagArg for hir::def::Namespace {
 }
 
 #[derive(Clone)]
-pub struct DiagSymbolList(Vec<Symbol>);
+pub struct DiagSymbolList<S = Symbol>(Vec<S>);
 
-impl From<Vec<Symbol>> for DiagSymbolList {
-    fn from(v: Vec<Symbol>) -> Self {
+impl<S> From<Vec<S>> for DiagSymbolList<S> {
+    fn from(v: Vec<S>) -> Self {
         DiagSymbolList(v)
     }
 }
 
-impl IntoDiagArg for DiagSymbolList {
+impl<S> FromIterator<S> for DiagSymbolList<S> {
+    fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
+        iter.into_iter().collect::<Vec<_>>().into()
+    }
+}
+
+impl<S: std::fmt::Display> IntoDiagArg for DiagSymbolList<S> {
     fn into_diag_arg(self) -> DiagArgValue {
         DiagArgValue::StrListSepByAnd(
             self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(),
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 45118bcc58a..aa47ca16676 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -2273,9 +2273,26 @@ impl HumanEmitter {
                     &normalize_whitespace(last_line),
                     Style::NoStyle,
                 );
-                buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
-                buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
-                buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
+                if !line_to_add.trim().is_empty() {
+                    // Check if after the removal, the line is left with only whitespace. If so, we
+                    // will not show an "addition" line, as removing the whole line is what the user
+                    // would really want.
+                    // For example, for the following:
+                    //   |
+                    // 2 -     .await
+                    // 2 +     (note the left over whitepsace)
+                    //   |
+                    // We really want
+                    //   |
+                    // 2 -     .await
+                    //   |
+                    // *row_num -= 1;
+                    buffer.puts(*row_num, 0, &self.maybe_anonymized(line_num), Style::LineNumber);
+                    buffer.puts(*row_num, max_line_num_len + 1, "+ ", Style::Addition);
+                    buffer.append(*row_num, &normalize_whitespace(line_to_add), Style::NoStyle);
+                } else {
+                    *row_num -= 1;
+                }
             } else {
                 *row_num -= 2;
             }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 91112a57277..2086d4030f9 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -63,6 +63,7 @@ use rustc_span::source_map::SourceMap;
 use rustc_span::{Loc, Span, DUMMY_SP};
 use std::backtrace::{Backtrace, BacktraceStatus};
 use std::borrow::Cow;
+use std::cell::Cell;
 use std::error::Report;
 use std::fmt;
 use std::hash::Hash;
@@ -98,9 +99,9 @@ rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
 
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
-rustc_data_structures::static_assert_size!(PResult<'_, ()>, 16);
+rustc_data_structures::static_assert_size!(PResult<'_, ()>, 24);
 #[cfg(target_pointer_width = "64")]
-rustc_data_structures::static_assert_size!(PResult<'_, bool>, 16);
+rustc_data_structures::static_assert_size!(PResult<'_, bool>, 24);
 
 #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, Encodable, Decodable)]
 pub enum SuggestionStyle {
@@ -417,6 +418,9 @@ pub struct DiagCtxt {
 #[derive(Copy, Clone)]
 pub struct DiagCtxtHandle<'a> {
     dcx: &'a DiagCtxt,
+    /// Some contexts create `DiagCtxtHandle` with this field set, and thus all
+    /// errors emitted with it will automatically taint when emitting errors.
+    tainted_with_errors: Option<&'a Cell<Option<ErrorGuaranteed>>>,
 }
 
 impl<'a> std::ops::Deref for DiagCtxtHandle<'a> {
@@ -752,7 +756,17 @@ impl DiagCtxt {
     }
 
     pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> {
-        DiagCtxtHandle { dcx: self }
+        DiagCtxtHandle { dcx: self, tainted_with_errors: None }
+    }
+
+    /// Link this to a taintable context so that emitting errors will automatically set
+    /// the `Option<ErrorGuaranteed>` instead of having to do that manually at every error
+    /// emission site.
+    pub fn taintable_handle<'a>(
+        &'a self,
+        tainted_with_errors: &'a Cell<Option<ErrorGuaranteed>>,
+    ) -> DiagCtxtHandle<'a> {
+        DiagCtxtHandle { dcx: self, tainted_with_errors: Some(tainted_with_errors) }
     }
 }
 
@@ -795,7 +809,9 @@ impl<'a> DiagCtxtHandle<'a> {
             // can be used to create a backtrace at the stashing site insted of whenever the
             // diagnostic context is dropped and thus delayed bugs are emitted.
             Error => Some(self.span_delayed_bug(span, format!("stashing {key:?}"))),
-            DelayedBug => return self.inner.borrow_mut().emit_diagnostic(diag),
+            DelayedBug => {
+                return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
+            }
             ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
             | Expect(_) => None,
         };
@@ -947,16 +963,19 @@ impl<'a> DiagCtxtHandle<'a> {
             (0, _) => {
                 // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
                 // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
-                inner.emit_diagnostic(DiagInner::new(
-                    ForceWarning(None),
-                    DiagMessage::Str(warnings),
-                ));
+                inner.emit_diagnostic(
+                    DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)),
+                    None,
+                );
             }
             (_, 0) => {
-                inner.emit_diagnostic(DiagInner::new(Error, errors));
+                inner.emit_diagnostic(DiagInner::new(Error, errors), self.tainted_with_errors);
             }
             (_, _) => {
-                inner.emit_diagnostic(DiagInner::new(Error, format!("{errors}; {warnings}")));
+                inner.emit_diagnostic(
+                    DiagInner::new(Error, format!("{errors}; {warnings}")),
+                    self.tainted_with_errors,
+                );
             }
         }
 
@@ -987,14 +1006,14 @@ impl<'a> DiagCtxtHandle<'a> {
                         "For more information about an error, try `rustc --explain {}`.",
                         &error_codes[0]
                     );
-                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg1));
-                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg2));
+                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg1), None);
+                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg2), None);
                 } else {
                     let msg = format!(
                         "For more information about this error, try `rustc --explain {}`.",
                         &error_codes[0]
                     );
-                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg));
+                    inner.emit_diagnostic(DiagInner::new(FailureNote, msg), None);
                 }
             }
         }
@@ -1020,7 +1039,7 @@ impl<'a> DiagCtxtHandle<'a> {
     }
 
     pub fn emit_diagnostic(&self, diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
-        self.inner.borrow_mut().emit_diagnostic(diagnostic)
+        self.inner.borrow_mut().emit_diagnostic(diagnostic, self.tainted_with_errors)
     }
 
     pub fn emit_artifact_notification(&self, path: &Path, artifact_type: &str) {
@@ -1080,7 +1099,7 @@ impl<'a> DiagCtxtHandle<'a> {
                 // Here the diagnostic is given back to `emit_diagnostic` where it was first
                 // intercepted. Now it should be processed as usual, since the unstable expectation
                 // id is now stable.
-                inner.emit_diagnostic(diag);
+                inner.emit_diagnostic(diag, self.tainted_with_errors);
             }
         }
 
@@ -1430,13 +1449,17 @@ impl DiagCtxtInner {
                     continue;
                 }
             }
-            guar = guar.or(self.emit_diagnostic(diag));
+            guar = guar.or(self.emit_diagnostic(diag, None));
         }
         guar
     }
 
     // Return value is only `Some` if the level is `Error` or `DelayedBug`.
-    fn emit_diagnostic(&mut self, mut diagnostic: DiagInner) -> Option<ErrorGuaranteed> {
+    fn emit_diagnostic(
+        &mut self,
+        mut diagnostic: DiagInner,
+        taint: Option<&Cell<Option<ErrorGuaranteed>>>,
+    ) -> Option<ErrorGuaranteed> {
         match diagnostic.level {
             Expect(expect_id) | ForceWarning(Some(expect_id)) => {
                 // The `LintExpectationId` can be stable or unstable depending on when it was
@@ -1609,6 +1632,9 @@ impl DiagCtxtInner {
                 if is_lint {
                     self.lint_err_guars.push(guar);
                 } else {
+                    if let Some(taint) = taint {
+                        taint.set(Some(guar));
+                    }
                     self.err_guars.push(guar);
                 }
                 self.panic_if_treat_err_as_bug();
@@ -1718,8 +1744,8 @@ impl DiagCtxtInner {
         // `-Ztreat-err-as-bug`, which we don't want.
         let note1 = "no errors encountered even though delayed bugs were created";
         let note2 = "those delayed bugs will now be shown as internal compiler errors";
-        self.emit_diagnostic(DiagInner::new(Note, note1));
-        self.emit_diagnostic(DiagInner::new(Note, note2));
+        self.emit_diagnostic(DiagInner::new(Note, note1), None);
+        self.emit_diagnostic(DiagInner::new(Note, note2), None);
 
         for bug in bugs {
             if let Some(out) = &mut out {
@@ -1752,7 +1778,7 @@ impl DiagCtxtInner {
             }
             bug.level = Bug;
 
-            self.emit_diagnostic(bug);
+            self.emit_diagnostic(bug, None);
         }
 
         // Panic with `DelayedBugPanic` to avoid "unexpected panic" messages.
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 56cbb54fcec..40e16b45115 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -172,7 +172,7 @@ impl<'a> StripUnconfigured<'a> {
     fn configure_tokens(&self, stream: &AttrTokenStream) -> AttrTokenStream {
         fn can_skip(stream: &AttrTokenStream) -> bool {
             stream.0.iter().all(|tree| match tree {
-                AttrTokenTree::Attributes(_) => false,
+                AttrTokenTree::AttrsTarget(_) => false,
                 AttrTokenTree::Token(..) => true,
                 AttrTokenTree::Delimited(.., inner) => can_skip(inner),
             })
@@ -185,22 +185,22 @@ impl<'a> StripUnconfigured<'a> {
         let trees: Vec<_> = stream
             .0
             .iter()
-            .flat_map(|tree| match tree.clone() {
-                AttrTokenTree::Attributes(mut data) => {
-                    data.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
+            .filter_map(|tree| match tree.clone() {
+                AttrTokenTree::AttrsTarget(mut target) => {
+                    target.attrs.flat_map_in_place(|attr| self.process_cfg_attr(&attr));
 
-                    if self.in_cfg(&data.attrs) {
-                        data.tokens = LazyAttrTokenStream::new(
-                            self.configure_tokens(&data.tokens.to_attr_token_stream()),
+                    if self.in_cfg(&target.attrs) {
+                        target.tokens = LazyAttrTokenStream::new(
+                            self.configure_tokens(&target.tokens.to_attr_token_stream()),
                         );
-                        Some(AttrTokenTree::Attributes(data)).into_iter()
+                        Some(AttrTokenTree::AttrsTarget(target))
                     } else {
-                        None.into_iter()
+                        None
                     }
                 }
                 AttrTokenTree::Delimited(sp, spacing, delim, mut inner) => {
                     inner = self.configure_tokens(&inner);
-                    Some(AttrTokenTree::Delimited(sp, spacing, delim, inner)).into_iter()
+                    Some(AttrTokenTree::Delimited(sp, spacing, delim, inner))
                 }
                 AttrTokenTree::Token(
                     Token {
@@ -220,9 +220,7 @@ impl<'a> StripUnconfigured<'a> {
                 ) => {
                     panic!("Should be `AttrTokenTree::Delimited`, not delim tokens: {:?}", tree);
                 }
-                AttrTokenTree::Token(token, spacing) => {
-                    Some(AttrTokenTree::Token(token, spacing)).into_iter()
-                }
+                AttrTokenTree::Token(token, spacing) => Some(AttrTokenTree::Token(token, spacing)),
             })
             .collect();
         AttrTokenStream::new(trees)
@@ -294,7 +292,7 @@ impl<'a> StripUnconfigured<'a> {
         attr: &Attribute,
         (item, item_span): (ast::AttrItem, Span),
     ) -> Attribute {
-        let orig_tokens = attr.tokens();
+        let orig_tokens = attr.get_tokens();
 
         // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
         // and producing an attribute of the form `#[attr]`. We
@@ -310,12 +308,11 @@ impl<'a> StripUnconfigured<'a> {
         else {
             panic!("Bad tokens for attribute {attr:?}");
         };
-        let pound_span = pound_token.span;
 
         // We don't really have a good span to use for the synthesized `[]`
         // in `#[attr]`, so just use the span of the `#` token.
         let bracket_group = AttrTokenTree::Delimited(
-            DelimSpan::from_single(pound_span),
+            DelimSpan::from_single(pound_token.span),
             DelimSpacing::new(Spacing::JointHidden, Spacing::Alone),
             Delimiter::Bracket,
             item.tokens
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index bf475c1dc96..2df8b8f00f8 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -120,21 +120,21 @@ struct CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
 
 struct BestFailure {
     token: Token,
-    position_in_tokenstream: usize,
+    position_in_tokenstream: u32,
     msg: &'static str,
     remaining_matcher: MatcherLoc,
 }
 
 impl BestFailure {
-    fn is_better_position(&self, position: usize) -> bool {
+    fn is_better_position(&self, position: u32) -> bool {
         position > self.position_in_tokenstream
     }
 }
 
 impl<'a, 'cx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'a, 'cx, 'matcher> {
-    type Failure = (Token, usize, &'static str);
+    type Failure = (Token, u32, &'static str);
 
-    fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure {
+    fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure {
         (tok, position, msg)
     }
 
@@ -211,9 +211,9 @@ impl<'matcher> FailureForwarder<'matcher> {
 }
 
 impl<'matcher> Tracker<'matcher> for FailureForwarder<'matcher> {
-    type Failure = (Token, usize, &'static str);
+    type Failure = (Token, u32, &'static str);
 
-    fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure {
+    fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure {
         (tok, position, msg)
     }
 
diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs
index 2fbd09fd9ae..99a9d4f8912 100644
--- a/compiler/rustc_expand/src/mbe/macro_parser.rs
+++ b/compiler/rustc_expand/src/mbe/macro_parser.rs
@@ -452,7 +452,7 @@ impl TtParser {
         &mut self,
         matcher: &'matcher [MatcherLoc],
         token: &Token,
-        approx_position: usize,
+        approx_position: u32,
         track: &mut T,
     ) -> Option<NamedParseResult<T::Failure>> {
         // Matcher positions that would be valid if the macro invocation was over now. Only
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index e43ba7c3a5a..88ec3d83664 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -153,7 +153,7 @@ pub(super) trait Tracker<'matcher> {
     /// Arm failed to match. If the token is `token::Eof`, it indicates an unexpected
     /// end of macro invocation. Otherwise, it indicates that no rules expected the given token.
     /// The usize is the approximate position of the token in the input token stream.
-    fn build_failure(tok: Token, position: usize, msg: &'static str) -> Self::Failure;
+    fn build_failure(tok: Token, position: u32, msg: &'static str) -> Self::Failure;
 
     /// This is called before trying to match next MatcherLoc on the current token.
     fn before_match_loc(&mut self, _parser: &TtParser, _matcher: &'matcher MatcherLoc) {}
@@ -182,7 +182,7 @@ pub(super) struct NoopTracker;
 impl<'matcher> Tracker<'matcher> for NoopTracker {
     type Failure = ();
 
-    fn build_failure(_tok: Token, _position: usize, _msg: &'static str) -> Self::Failure {}
+    fn build_failure(_tok: Token, _position: u32, _msg: &'static str) -> Self::Failure {}
 
     fn description() -> &'static str {
         "none"
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 25958e03028..dbbd948fd70 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token::{self, Delimiter, IdentIsRaw};
+use rustc_ast::token::{self, Delimiter, IdentIsRaw, Lit, Token, TokenKind};
 use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
 use rustc_ast::{LitIntType, LitKind};
 use rustc_ast_pretty::pprust;
@@ -6,9 +6,10 @@ use rustc_errors::{Applicability, PResult};
 use rustc_macros::{Decodable, Encodable};
 use rustc_session::parse::ParseSess;
 use rustc_span::symbol::Ident;
-use rustc_span::Span;
+use rustc_span::{Span, Symbol};
 
 pub(crate) const RAW_IDENT_ERR: &str = "`${concat(..)}` currently does not support raw identifiers";
+pub(crate) const UNSUPPORTED_CONCAT_ELEM_ERR: &str = "expected identifier or string literal";
 
 /// A meta-variable expression, for expansions based on properties of meta-variables.
 #[derive(Debug, PartialEq, Encodable, Decodable)]
@@ -51,11 +52,26 @@ impl MetaVarExpr {
                 let mut result = Vec::new();
                 loop {
                     let is_var = try_eat_dollar(&mut iter);
-                    let element_ident = parse_ident(&mut iter, psess, outer_span)?;
+                    let token = parse_token(&mut iter, psess, outer_span)?;
                     let element = if is_var {
-                        MetaVarExprConcatElem::Var(element_ident)
+                        MetaVarExprConcatElem::Var(parse_ident_from_token(psess, token)?)
+                    } else if let TokenKind::Literal(Lit {
+                        kind: token::LitKind::Str,
+                        symbol,
+                        suffix: None,
+                    }) = token.kind
+                    {
+                        MetaVarExprConcatElem::Literal(symbol)
                     } else {
-                        MetaVarExprConcatElem::Ident(element_ident)
+                        match parse_ident_from_token(psess, token) {
+                            Err(err) => {
+                                err.cancel();
+                                return Err(psess
+                                    .dcx()
+                                    .struct_span_err(token.span, UNSUPPORTED_CONCAT_ELEM_ERR));
+                            }
+                            Ok(elem) => MetaVarExprConcatElem::Ident(elem),
+                        }
                     };
                     result.push(element);
                     if iter.look_ahead(0).is_none() {
@@ -105,11 +121,13 @@ impl MetaVarExpr {
 
 #[derive(Debug, Decodable, Encodable, PartialEq)]
 pub(crate) enum MetaVarExprConcatElem {
-    /// There is NO preceding dollar sign, which means that this identifier should be interpreted
-    /// as a literal.
+    /// Identifier WITHOUT a preceding dollar sign, which means that this identifier should be
+    /// interpreted as a literal.
     Ident(Ident),
-    /// There is a preceding dollar sign, which means that this identifier should be expanded
-    /// and interpreted as a variable.
+    /// For example, a number or a string.
+    Literal(Symbol),
+    /// Identifier WITH a preceding dollar sign, which means that this identifier should be
+    /// expanded and interpreted as a variable.
     Var(Ident),
 }
 
@@ -158,7 +176,7 @@ fn parse_depth<'psess>(
     span: Span,
 ) -> PResult<'psess, usize> {
     let Some(tt) = iter.next() else { return Ok(0) };
-    let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else {
+    let TokenTree::Token(Token { kind: TokenKind::Literal(lit), .. }, _) = tt else {
         return Err(psess
             .dcx()
             .struct_span_err(span, "meta-variable expression depth must be a literal"));
@@ -180,12 +198,14 @@ fn parse_ident<'psess>(
     psess: &'psess ParseSess,
     fallback_span: Span,
 ) -> PResult<'psess, Ident> {
-    let Some(tt) = iter.next() else {
-        return Err(psess.dcx().struct_span_err(fallback_span, "expected identifier"));
-    };
-    let TokenTree::Token(token, _) = tt else {
-        return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier"));
-    };
+    let token = parse_token(iter, psess, fallback_span)?;
+    parse_ident_from_token(psess, token)
+}
+
+fn parse_ident_from_token<'psess>(
+    psess: &'psess ParseSess,
+    token: &Token,
+) -> PResult<'psess, Ident> {
     if let Some((elem, is_raw)) = token.ident() {
         if let IdentIsRaw::Yes = is_raw {
             return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
@@ -205,10 +225,24 @@ fn parse_ident<'psess>(
     Err(err)
 }
 
+fn parse_token<'psess, 't>(
+    iter: &mut RefTokenTreeCursor<'t>,
+    psess: &'psess ParseSess,
+    fallback_span: Span,
+) -> PResult<'psess, &'t Token> {
+    let Some(tt) = iter.next() else {
+        return Err(psess.dcx().struct_span_err(fallback_span, UNSUPPORTED_CONCAT_ELEM_ERR));
+    };
+    let TokenTree::Token(token, _) = tt else {
+        return Err(psess.dcx().struct_span_err(tt.span(), UNSUPPORTED_CONCAT_ELEM_ERR));
+    };
+    Ok(token)
+}
+
 /// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
 /// iterator is not modified and the result is `false`.
 fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
-    if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
+    if let Some(TokenTree::Token(Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
         let _ = iter.next();
         return true;
     }
@@ -218,8 +252,7 @@ fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
 /// Tries to move the iterator forward returning `true` if there is a dollar sign. If not, then the
 /// iterator is not modified and the result is `false`.
 fn try_eat_dollar(iter: &mut RefTokenTreeCursor<'_>) -> bool {
-    if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
-    {
+    if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
         let _ = iter.next();
         return true;
     }
@@ -232,8 +265,7 @@ fn eat_dollar<'psess>(
     psess: &'psess ParseSess,
     span: Span,
 ) -> PResult<'psess, ()> {
-    if let Some(TokenTree::Token(token::Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0)
-    {
+    if let Some(TokenTree::Token(Token { kind: token::Dollar, .. }, _)) = iter.look_ahead(0) {
         let _ = iter.next();
         return Ok(());
     }
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index f935f1b77e0..9b4dc13c703 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -11,11 +11,13 @@ use rustc_ast::token::{self, Delimiter, Token, TokenKind};
 use rustc_ast::tokenstream::{DelimSpacing, DelimSpan, Spacing, TokenStream, TokenTree};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult};
+use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parser::ParseNtResult;
 use rustc_session::parse::ParseSess;
+use rustc_session::parse::SymbolGallery;
 use rustc_span::hygiene::{LocalExpnId, Transparency};
 use rustc_span::symbol::{sym, Ident, MacroRulesNormalizedIdent};
-use rustc_span::{with_metavar_spans, Span, Symbol, SyntaxContext};
+use rustc_span::{with_metavar_spans, Span, SyntaxContext};
 use smallvec::{smallvec, SmallVec};
 use std::mem;
 
@@ -312,7 +314,16 @@ pub(super) fn transcribe<'a>(
 
             // Replace meta-variable expressions with the result of their expansion.
             mbe::TokenTree::MetaVarExpr(sp, expr) => {
-                transcribe_metavar_expr(dcx, expr, interp, &mut marker, &repeats, &mut result, sp)?;
+                transcribe_metavar_expr(
+                    dcx,
+                    expr,
+                    interp,
+                    &mut marker,
+                    &repeats,
+                    &mut result,
+                    sp,
+                    &psess.symbol_gallery,
+                )?;
             }
 
             // If we are entering a new delimiter, we push its contents to the `stack` to be
@@ -669,6 +680,7 @@ fn transcribe_metavar_expr<'a>(
     repeats: &[(usize, usize)],
     result: &mut Vec<TokenTree>,
     sp: &DelimSpan,
+    symbol_gallery: &SymbolGallery,
 ) -> PResult<'a, ()> {
     let mut visited_span = || {
         let mut span = sp.entire();
@@ -680,16 +692,26 @@ fn transcribe_metavar_expr<'a>(
             let mut concatenated = String::new();
             for element in elements.into_iter() {
                 let string = match element {
-                    MetaVarExprConcatElem::Ident(ident) => ident.to_string(),
-                    MetaVarExprConcatElem::Var(ident) => extract_ident(dcx, *ident, interp)?,
+                    MetaVarExprConcatElem::Ident(elem) => elem.to_string(),
+                    MetaVarExprConcatElem::Literal(elem) => elem.as_str().into(),
+                    MetaVarExprConcatElem::Var(elem) => extract_ident(dcx, *elem, interp)?,
                 };
                 concatenated.push_str(&string);
             }
+            let symbol = nfc_normalize(&concatenated);
+            let concatenated_span = visited_span();
+            if !rustc_lexer::is_ident(symbol.as_str()) {
+                return Err(dcx.struct_span_err(
+                    concatenated_span,
+                    "`${concat(..)}` is not generating a valid identifier",
+                ));
+            }
+            symbol_gallery.insert(symbol, concatenated_span);
             // The current implementation marks the span as coming from the macro regardless of
             // contexts of the concatenated identifiers but this behavior may change in the
             // future.
             result.push(TokenTree::Token(
-                Token::from_ast_ident(Ident::new(Symbol::intern(&concatenated), visited_span())),
+                Token::from_ast_ident(Ident::new(symbol, concatenated_span)),
                 Spacing::Alone,
             ));
         }
diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs
index 32a047a9363..7b27049a579 100644
--- a/compiler/rustc_feature/src/builtin_attrs.rs
+++ b/compiler/rustc_feature/src/builtin_attrs.rs
@@ -556,7 +556,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
     ),
     // RFC 2632
     gated!(
-        const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::Yes, const_trait_impl,
+        const_trait, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No, const_trait_impl,
         "`const_trait` is a temporary placeholder for marking a trait that is suitable for `const` \
         `impls` and all default bodies as `const`, which may be removed or renamed in the \
         future."
@@ -585,6 +585,13 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         EncodeCrossCrate::No, derive_smart_pointer, experimental!(pointee)
     ),
 
+    // RFC 3543
+    // `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]`
+    gated!(
+        patchable_function_entry, Normal, template!(List: "prefix_nops = m, entry_nops = n"), ErrorPreceding,
+        EncodeCrossCrate::Yes, experimental!(patchable_function_entry)
+    ),
+
     // ==========================================================================
     // Internal attributes: Stability, deprecation, and unsafe:
     // ==========================================================================
@@ -826,6 +833,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         rustc_const_panic_str, Normal, template!(Word), WarnFollowing,
         EncodeCrossCrate::Yes, INTERNAL_UNSTABLE
     ),
+    rustc_attr!(
+        rustc_runtime, Normal, template!(Word), WarnFollowing,
+        EncodeCrossCrate::No, INTERNAL_UNSTABLE
+    ),
 
     // ==========================================================================
     // Internal attributes, Layout related:
@@ -1107,6 +1118,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
         WarnFollowing, EncodeCrossCrate::No
     ),
     rustc_attr!(
+        TEST, rustc_dump_def_parents, Normal, template!(Word),
+        WarnFollowing, EncodeCrossCrate::No
+    ),
+    rustc_attr!(
         TEST, rustc_object_lifetime_default, Normal, template!(Word),
         WarnFollowing, EncodeCrossCrate::No
     ),
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index f4e20328814..c05cac155b7 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -563,6 +563,8 @@ declare_features! (
     (unstable, offset_of_slice, "CURRENT_RUSTC_VERSION", Some(126151)),
     /// Allows using `#[optimize(X)]`.
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
+    /// Allows specifying nop padding on functions for dynamic patching.
+    (unstable, patchable_function_entry, "CURRENT_RUSTC_VERSION", Some(123115)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
     /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
@@ -573,6 +575,8 @@ declare_features! (
     (unstable, raw_ref_op, "1.41.0", Some(64490)),
     /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024.
     (incomplete, ref_pat_eat_one_layer_2024, "1.79.0", Some(123076)),
+    /// Makes `&` and `&mut` patterns eat only one layer of references in Rust 2024—structural variant
+    (incomplete, ref_pat_eat_one_layer_2024_structural, "CURRENT_RUSTC_VERSION", Some(123076)),
     /// Allows using the `#[register_tool]` attribute.
     (unstable, register_tool, "1.41.0", Some(66079)),
     /// Allows the `#[repr(i128)]` attribute for enums.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 22a6c06bba3..d57fad6ba4c 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -526,6 +526,7 @@ pub enum GenericParamKind<'hir> {
         /// Optional default value for the const generic param
         default: Option<&'hir AnonConst>,
         is_host_effect: bool,
+        synthetic: bool,
     },
 }
 
@@ -2413,7 +2414,7 @@ pub enum ImplItemKind<'hir> {
 /// * the `A: Bound` in `Trait<A: Bound>`
 /// * the `RetTy` in `Trait(ArgTy, ArgTy) -> RetTy`
 /// * the `C = { Ct }` in `Trait<C = { Ct }>` (feature `associated_const_equality`)
-/// * the `f(): Bound` in `Trait<f(): Bound>` (feature `return_type_notation`)
+/// * the `f(..): Bound` in `Trait<f(..): Bound>` (feature `return_type_notation`)
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct AssocItemConstraint<'hir> {
     pub hir_id: HirId,
@@ -3364,6 +3365,7 @@ pub enum ItemKind<'hir> {
 /// Refer to [`ImplItem`] for an associated item within an impl block.
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
 pub struct Impl<'hir> {
+    pub constness: Constness,
     pub safety: Safety,
     pub polarity: ImplPolarity,
     pub defaultness: Defaultness,
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 065ecc5d7b7..9bb3245ae05 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -543,6 +543,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_enum_def(enum_definition, item.hir_id()));
         }
         ItemKind::Impl(Impl {
+            constness: _,
             safety: _,
             defaultness: _,
             polarity: _,
@@ -915,7 +916,7 @@ pub fn walk_generic_param<'v, V: Visitor<'v>>(
     match param.kind {
         GenericParamKind::Lifetime { .. } => {}
         GenericParamKind::Type { ref default, .. } => visit_opt!(visitor, visit_ty, default),
-        GenericParamKind::Const { ref ty, ref default, is_host_effect: _ } => {
+        GenericParamKind::Const { ref ty, ref default, is_host_effect: _, synthetic: _ } => {
             try_visit!(visitor.visit_ty(ty));
             if let Some(ref default) = default {
                 try_visit!(visitor.visit_const_param_default(param.hir_id, default));
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index 69461957f80..30c0e40206a 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -11,6 +11,7 @@ use crate::def_id::DefId;
 use crate::{MethodKind, Target};
 
 use rustc_ast as ast;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::symbol::{kw, sym, Symbol};
@@ -23,6 +24,7 @@ pub struct LanguageItems {
     /// Mappings from lang items to their possibly found [`DefId`]s.
     /// The index corresponds to the order in [`LangItem`].
     items: [Option<DefId>; std::mem::variant_count::<LangItem>()],
+    reverse_items: FxIndexMap<DefId, LangItem>,
     /// Lang items that were not found during collection.
     pub missing: Vec<LangItem>,
 }
@@ -30,7 +32,11 @@ pub struct LanguageItems {
 impl LanguageItems {
     /// Construct an empty collection of lang items and no missing ones.
     pub fn new() -> Self {
-        Self { items: [None; std::mem::variant_count::<LangItem>()], missing: Vec::new() }
+        Self {
+            items: [None; std::mem::variant_count::<LangItem>()],
+            reverse_items: FxIndexMap::default(),
+            missing: Vec::new(),
+        }
     }
 
     pub fn get(&self, item: LangItem) -> Option<DefId> {
@@ -39,6 +45,11 @@ impl LanguageItems {
 
     pub fn set(&mut self, item: LangItem, def_id: DefId) {
         self.items[item as usize] = Some(def_id);
+        self.reverse_items.insert(def_id, item);
+    }
+
+    pub fn from_def_id(&self, def_id: DefId) -> Option<LangItem> {
+        self.reverse_items.get(&def_id).copied()
     }
 
     pub fn iter(&self) -> impl Iterator<Item = (LangItem, DefId)> + '_ {
@@ -393,6 +404,14 @@ language_item_table! {
 
     String,                  sym::String,              string,                     Target::Struct,         GenericRequirement::None;
     CStr,                    sym::CStr,                c_str,                      Target::Struct,         GenericRequirement::None;
+
+    EffectsRuntime,          sym::EffectsRuntime,      effects_runtime,            Target::Struct,         GenericRequirement::None;
+    EffectsNoRuntime,        sym::EffectsNoRuntime,    effects_no_runtime,         Target::Struct,         GenericRequirement::None;
+    EffectsMaybe,            sym::EffectsMaybe,        effects_maybe,              Target::Struct,         GenericRequirement::None;
+    EffectsIntersection,     sym::EffectsIntersection, effects_intersection,       Target::Trait,          GenericRequirement::None;
+    EffectsIntersectionOutput, sym::EffectsIntersectionOutput, effects_intersection_output, Target::AssocTy, GenericRequirement::None;
+    EffectsCompat,           sym::EffectsCompat,       effects_compat,             Target::Trait,          GenericRequirement::Exact(1);
+    EffectsTyCompat,         sym::EffectsTyCompat,     effects_ty_compat,          Target::Trait,          GenericRequirement::Exact(1);
 }
 
 pub enum GenericRequirement {
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index 7ed32fb9d9f..24c5377a3b1 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -55,8 +55,6 @@ hir_analysis_cannot_capture_late_bound_ty =
     cannot capture late-bound type parameter in {$what}
     .label = parameter defined here
 
-hir_analysis_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}`
-
 hir_analysis_closure_implicit_hrtb = implicit types in closure signatures are forbidden when `for<...>` is present
     .label = `for<...>` is here
 
@@ -120,6 +118,10 @@ hir_analysis_drop_impl_reservation = reservation `Drop` impls are not supported
 hir_analysis_duplicate_precise_capture = cannot capture parameter `{$name}` twice
     .label = parameter captured again here
 
+hir_analysis_effects_without_next_solver = using `#![feature(effects)]` without enabling next trait solver globally
+    .note = the next trait solver must be enabled globally for the effects feature to work correctly
+    .help = use `-Znext-solver` to enable
+
 hir_analysis_empty_specialization = specialization impl does not specialize any associated items
     .note = impl is a specialization of this impl
 
@@ -371,10 +373,6 @@ hir_analysis_paren_sugar_attribute = the `#[rustc_paren_sugar]` attribute is a t
 hir_analysis_parenthesized_fn_trait_expansion =
     parenthesized trait syntax expands to `{$expanded_type}`
 
-hir_analysis_pass_to_variadic_function = can't pass `{$ty}` to variadic function
-    .suggestion = cast the value to `{$cast_ty}`
-    .help = cast the value to `{$cast_ty}`
-
 hir_analysis_pattern_type_non_const_range = range patterns must have constant range start and end
 hir_analysis_pattern_type_wild_pat = wildcard patterns are not permitted for pattern types
     .label = this type is the same as the inner type without a pattern
diff --git a/compiler/rustc_hir_analysis/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 7f0d72b3a8d..c30a6f1eeb9 100644
--- a/compiler/rustc_hir_analysis/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
@@ -1,8 +1,12 @@
 //! Bounds are restrictions applied to some types after they've been lowered from the HIR to the
 //! [`rustc_middle::ty`] form.
 
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_hir::def::DefKind;
 use rustc_hir::LangItem;
+use rustc_middle::ty::fold::FnMutDelegate;
 use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
+use rustc_span::def_id::DefId;
 use rustc_span::Span;
 
 /// Collects together a list of type bounds. These lists of bounds occur in many places
@@ -24,6 +28,7 @@ use rustc_span::Span;
 #[derive(Default, PartialEq, Eq, Clone, Debug)]
 pub struct Bounds<'tcx> {
     clauses: Vec<(ty::Clause<'tcx>, Span)>,
+    effects_min_tys: FxIndexMap<Ty<'tcx>, Span>,
 }
 
 impl<'tcx> Bounds<'tcx> {
@@ -40,12 +45,14 @@ impl<'tcx> Bounds<'tcx> {
     pub fn push_trait_bound(
         &mut self,
         tcx: TyCtxt<'tcx>,
-        trait_ref: ty::PolyTraitRef<'tcx>,
+        defining_def_id: DefId,
+        bound_trait_ref: ty::PolyTraitRef<'tcx>,
         span: Span,
         polarity: ty::PredicatePolarity,
+        constness: ty::BoundConstness,
     ) {
         let clause = (
-            trait_ref
+            bound_trait_ref
                 .map_bound(|trait_ref| {
                     ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, polarity })
                 })
@@ -53,11 +60,98 @@ impl<'tcx> Bounds<'tcx> {
             span,
         );
         // FIXME(-Znext-solver): We can likely remove this hack once the new trait solver lands.
-        if tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
+        if tcx.is_lang_item(bound_trait_ref.def_id(), LangItem::Sized) {
             self.clauses.insert(0, clause);
         } else {
             self.clauses.push(clause);
         }
+
+        if !tcx.features().effects {
+            return;
+        }
+        // For `T: ~const Tr` or `T: const Tr`, we need to add an additional bound on the
+        // associated type of `<T as Tr>` and make sure that the effect is compatible.
+        let compat_val = match (tcx.def_kind(defining_def_id), constness) {
+            // FIXME(effects): revisit the correctness of this
+            (_, ty::BoundConstness::Const) => tcx.consts.false_,
+            // body owners that can have trait bounds
+            (DefKind::Const | DefKind::Fn | DefKind::AssocFn, ty::BoundConstness::ConstIfConst) => {
+                tcx.expected_host_effect_param_for_body(defining_def_id)
+            }
+
+            (_, ty::BoundConstness::NotConst) => {
+                if !tcx.is_const_trait(bound_trait_ref.def_id()) {
+                    return;
+                }
+                tcx.consts.true_
+            }
+
+            (
+                DefKind::Trait | DefKind::Impl { of_trait: true },
+                ty::BoundConstness::ConstIfConst,
+            ) => {
+                // this is either a where clause on an impl/trait header or on a trait.
+                // push `<T as Tr>::Effects` into the set for the `Min` bound.
+                let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
+                    tcx.dcx().span_delayed_bug(span, "`~const` on trait without Effects assoc");
+                    return;
+                };
+
+                let ty = bound_trait_ref
+                    .map_bound(|trait_ref| Ty::new_projection(tcx, assoc, trait_ref.args));
+
+                // When the user has written `for<'a, T> X<'a, T>: ~const Foo`, replace the
+                // binders to dummy ones i.e. `X<'static, ()>` so they can be referenced in
+                // the `Min` associated type properly (which doesn't allow using `for<>`)
+                // This should work for any bound variables as long as they don't have any
+                // bounds e.g. `for<T: Trait>`.
+                // FIXME(effects) reconsider this approach to allow compatibility with `for<T: Tr>`
+                let ty = tcx.replace_bound_vars_uncached(
+                    ty,
+                    FnMutDelegate {
+                        regions: &mut |_| tcx.lifetimes.re_static,
+                        types: &mut |_| tcx.types.unit,
+                        consts: &mut |_| unimplemented!("`~const` does not support const binders"),
+                    },
+                );
+
+                self.effects_min_tys.insert(ty, span);
+                return;
+            }
+            // for
+            // ```
+            // trait Foo { type Bar: ~const Trait }
+            // ```
+            // ensure that `<Self::Bar as Trait>::Effects: TyCompat<Self::Effects>`.
+            //
+            // FIXME(effects) this is equality for now, which wouldn't be helpful for a non-const implementor
+            // that uses a `Bar` that implements `Trait` with `Maybe` effects.
+            (DefKind::AssocTy, ty::BoundConstness::ConstIfConst) => {
+                // FIXME(effects): implement this
+                return;
+            }
+            // probably illegal in this position.
+            (_, ty::BoundConstness::ConstIfConst) => {
+                tcx.dcx().span_delayed_bug(span, "invalid `~const` encountered");
+                return;
+            }
+        };
+        // create a new projection type `<T as Tr>::Effects`
+        let Some(assoc) = tcx.associated_type_for_effects(bound_trait_ref.def_id()) else {
+            tcx.dcx().span_delayed_bug(
+                span,
+                "`~const` trait bound has no effect assoc yet no errors encountered?",
+            );
+            return;
+        };
+        let self_ty = Ty::new_projection(tcx, assoc, bound_trait_ref.skip_binder().args);
+        // make `<T as Tr>::Effects: Compat<runtime>`
+        let new_trait_ref = ty::TraitRef::new(
+            tcx,
+            tcx.require_lang_item(LangItem::EffectsCompat, Some(span)),
+            [ty::GenericArg::from(self_ty), compat_val.into()],
+        );
+        self.clauses.push((bound_trait_ref.rebind(new_trait_ref).upcast(tcx), span));
     }
 
     pub fn push_projection_bound(
@@ -79,7 +173,15 @@ impl<'tcx> Bounds<'tcx> {
         self.clauses.insert(0, (trait_ref.upcast(tcx), span));
     }
 
-    pub fn clauses(&self) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
+    pub fn clauses(
+        &self,
+        // FIXME(effects): remove tcx
+        _tcx: TyCtxt<'tcx>,
+    ) -> impl Iterator<Item = (ty::Clause<'tcx>, Span)> + '_ {
         self.clauses.iter().cloned()
     }
+
+    pub fn effects_min_tys(&self) -> impl Iterator<Item = Ty<'tcx>> + '_ {
+        self.effects_min_tys.keys().copied()
+    }
 }
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index e13ea1a1935..bf8ef18c04f 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -25,9 +25,9 @@ use rustc_middle::ty::{
 };
 use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
 use rustc_target::abi::FieldIdx;
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedDirective;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_type_ir::fold::TypeFoldable;
 
@@ -879,7 +879,8 @@ pub(super) fn check_specialization_validity<'tcx>(
     let result = opt_result.unwrap_or(Ok(()));
 
     if let Err(parent_impl) = result {
-        if !tcx.is_impl_trait_in_trait(impl_item) {
+        // FIXME(effects) the associated type from effects could be specialized
+        if !tcx.is_impl_trait_in_trait(impl_item) && !tcx.is_effects_desugared_assoc_ty(impl_item) {
             report_forbidden_specialization(tcx, impl_item, parent_impl);
         } else {
             tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default"));
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 7fa5c96bc9a..6c53625b590 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -21,8 +21,9 @@ use rustc_middle::ty::{
 use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
 use rustc_middle::{bug, span_bug};
 use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::regions::InferCtxtRegionExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
@@ -981,7 +982,7 @@ fn report_trait_method_mismatch<'tcx>(
                 .next()
                 .unwrap_or(impl_err_span);
 
-            diag.span_suggestion(
+            diag.span_suggestion_verbose(
                 span,
                 "change the self-receiver type to match the trait",
                 sugg,
@@ -1005,12 +1006,12 @@ fn report_trait_method_mismatch<'tcx>(
                         }
                         hir::FnRetTy::Return(hir_ty) => {
                             let sugg = trait_sig.output();
-                            diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+                            diag.span_suggestion_verbose(hir_ty.span, msg, sugg, ap);
                         }
                     };
                 };
             } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
-                diag.span_suggestion(
+                diag.span_suggestion_verbose(
                     impl_err_span,
                     "change the parameter type to match the trait",
                     trait_ty,
@@ -1985,10 +1986,10 @@ pub(super) fn check_type_bounds<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
 
-    // A synthetic impl Trait for RPITIT desugaring has no HIR, which we currently use to get the
-    // span for an impl's associated type. Instead, for these, use the def_span for the synthesized
-    // associated type.
-    let impl_ty_span = if impl_ty.is_impl_trait_in_trait() {
+    // A synthetic impl Trait for RPITIT desugaring or assoc type for effects desugaring has no HIR,
+    // which we currently use to get the span for an impl's associated type. Instead, for these,
+    // use the def_span for the synthesized  associated type.
+    let impl_ty_span = if impl_ty.is_impl_trait_in_trait() || impl_ty.is_effects_desugaring {
         tcx.def_span(impl_ty_def_id)
     } else {
         match tcx.hir_node_by_def_id(impl_ty_def_id) {
diff --git a/compiler/rustc_hir_analysis/src/check/entry.rs b/compiler/rustc_hir_analysis/src/check/entry.rs
index cc52a765802..ce921f64481 100644
--- a/compiler/rustc_hir_analysis/src/check/entry.rs
+++ b/compiler/rustc_hir_analysis/src/check/entry.rs
@@ -7,7 +7,7 @@ use rustc_session::config::EntryFnType;
 use rustc_span::def_id::{DefId, LocalDefId, CRATE_DEF_ID};
 use rustc_span::{symbol::sym, Span};
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
 
 use std::ops::Not;
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index f21aeb4c0b9..6282499883b 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -26,15 +26,12 @@ fn equate_intrinsic_type<'tcx>(
     n_cts: usize,
     sig: ty::PolyFnSig<'tcx>,
 ) {
-    let (own_counts, span) = match tcx.hir_node_by_def_id(def_id) {
+    let (generics, span) = match tcx.hir_node_by_def_id(def_id) {
         hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, generics, _), .. })
         | hir::Node::ForeignItem(hir::ForeignItem {
             kind: hir::ForeignItemKind::Fn(.., generics, _),
             ..
-        }) => {
-            let own_counts = tcx.generics_of(def_id).own_counts();
-            (own_counts, generics.span)
-        }
+        }) => (tcx.generics_of(def_id), generics.span),
         _ => {
             struct_span_code_err!(tcx.dcx(), span, E0622, "intrinsic must be a function")
                 .with_span_label(span, "expected a function")
@@ -42,6 +39,7 @@ fn equate_intrinsic_type<'tcx>(
             return;
         }
     };
+    let own_counts = generics.own_counts();
 
     let gen_count_ok = |found: usize, expected: usize, descr: &str| -> bool {
         if found != expected {
@@ -57,9 +55,17 @@ fn equate_intrinsic_type<'tcx>(
         }
     };
 
+    // the host effect param should be invisible as it shouldn't matter
+    // whether effects is enabled for the intrinsic provider crate.
+    let consts_count = if generics.host_effect_index.is_some() {
+        own_counts.consts - 1
+    } else {
+        own_counts.consts
+    };
+
     if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime")
         && gen_count_ok(own_counts.types, n_tps, "type")
-        && gen_count_ok(own_counts.consts, n_cts, "const")
+        && gen_count_ok(consts_count, n_cts, "const")
     {
         let _ = check_function_signature(
             tcx,
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index 8469cbbbc7d..6a36938dd1d 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -96,10 +96,10 @@ use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{def_id::CRATE_DEF_ID, BytePos, Span, Symbol, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 use rustc_target::spec::abi::Abi;
-use rustc_trait_selection::traits::error_reporting::suggestions::{
+use rustc_trait_selection::error_reporting::traits::suggestions::{
     ReturnsVisitor, TypeErrCtxtExt as _,
 };
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::ObligationCtxt;
 
 use crate::errors;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index a188c1b12ae..b2ef07d65c5 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -29,8 +29,8 @@ use rustc_session::parse::feature_err;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::regions::InferCtxtRegionExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::misc::{
     type_allowed_to_implement_const_param_ty, ConstParamTyImplementationError,
 };
@@ -39,6 +39,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
 use rustc_trait_selection::traits::{
     self, FulfillmentError, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
 };
+use rustc_type_ir::solve::NoSolution;
 use rustc_type_ir::TypeFlags;
 
 use std::cell::LazyCell;
@@ -477,7 +478,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, trait_def_id: LocalDefId) {
                             param_env,
                             item_def_id,
                             tcx.explicit_item_bounds(item_def_id)
-                                .instantiate_identity_iter_copied()
+                                .iter_identity_copied()
                                 .collect::<Vec<_>>(),
                             &FxIndexSet::default(),
                             gat_def_id,
@@ -913,7 +914,12 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) -> Result<(),
         hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => Ok(()),
 
         // Const parameters are well formed if their type is structural match.
-        hir::GenericParamKind::Const { ty: hir_ty, default: _, is_host_effect: _ } => {
+        hir::GenericParamKind::Const {
+            ty: hir_ty,
+            default: _,
+            is_host_effect: _,
+            synthetic: _,
+        } => {
             let ty = tcx.type_of(param.def_id).instantiate_identity();
 
             if tcx.features().adt_const_params {
@@ -1199,17 +1205,16 @@ fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: ty::AssocIt
     let bounds = wfcx.tcx().explicit_item_bounds(item.def_id);
 
     debug!("check_associated_type_bounds: bounds={:?}", bounds);
-    let wf_obligations =
-        bounds.instantiate_identity_iter_copied().flat_map(|(bound, bound_span)| {
-            let normalized_bound = wfcx.normalize(span, None, bound);
-            traits::wf::clause_obligations(
-                wfcx.infcx,
-                wfcx.param_env,
-                wfcx.body_def_id,
-                normalized_bound,
-                bound_span,
-            )
-        });
+    let wf_obligations = bounds.iter_identity_copied().flat_map(|(bound, bound_span)| {
+        let normalized_bound = wfcx.normalize(span, None, bound);
+        traits::wf::clause_obligations(
+            wfcx.infcx,
+            wfcx.param_env,
+            wfcx.body_def_id,
+            normalized_bound,
+            bound_span,
+        )
+    });
 
     wfcx.register_obligations(wf_obligations);
 }
@@ -1707,13 +1712,12 @@ fn receiver_is_valid<'tcx>(
     let cause =
         ObligationCause::new(span, wfcx.body_def_id, traits::ObligationCauseCode::MethodReceiver);
 
-    let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty);
-
-    // `self: Self` is always valid.
-    if can_eq_self(receiver_ty) {
-        if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, receiver_ty) {
-            infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
-        }
+    // Special case `receiver == self_ty`, which doesn't necessarily require the `Receiver` lang item.
+    if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
+        let ocx = ObligationCtxt::new(wfcx.infcx);
+        ocx.eq(&cause, wfcx.param_env, self_ty, receiver_ty)?;
+        if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
+    }) {
         return true;
     }
 
@@ -1724,58 +1728,51 @@ fn receiver_is_valid<'tcx>(
         autoderef = autoderef.include_raw_pointers();
     }
 
-    // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it.
-    autoderef.next();
-
     let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, Some(span));
 
     // Keep dereferencing `receiver_ty` until we get to `self_ty`.
-    loop {
-        if let Some((potential_self_ty, _)) = autoderef.next() {
-            debug!(
-                "receiver_is_valid: potential self type `{:?}` to match `{:?}`",
-                potential_self_ty, self_ty
-            );
-
-            if can_eq_self(potential_self_ty) {
-                wfcx.register_obligations(autoderef.into_obligations());
+    while let Some((potential_self_ty, _)) = autoderef.next() {
+        debug!(
+            "receiver_is_valid: potential self type `{:?}` to match `{:?}`",
+            potential_self_ty, self_ty
+        );
 
-                if let Err(err) = wfcx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty) {
-                    infcx
-                        .err_ctxt()
-                        .report_mismatched_types(&cause, self_ty, potential_self_ty, err)
-                        .emit();
-                }
+        // Check if the self type unifies. If it does, then commit the result
+        // since it may have region side-effects.
+        if let Ok(()) = wfcx.infcx.commit_if_ok(|_| {
+            let ocx = ObligationCtxt::new(wfcx.infcx);
+            ocx.eq(&cause, wfcx.param_env, self_ty, potential_self_ty)?;
+            if ocx.select_all_or_error().is_empty() { Ok(()) } else { Err(NoSolution) }
+        }) {
+            wfcx.register_obligations(autoderef.into_obligations());
+            return true;
+        }
 
+        // Without `feature(arbitrary_self_types)`, we require that each step in the
+        // deref chain implement `receiver`.
+        if !arbitrary_self_types_enabled {
+            if !receiver_is_implemented(
+                wfcx,
+                receiver_trait_def_id,
+                cause.clone(),
+                potential_self_ty,
+            ) {
+                // We cannot proceed.
                 break;
-            } else {
-                // Without `feature(arbitrary_self_types)`, we require that each step in the
-                // deref chain implement `receiver`
-                if !arbitrary_self_types_enabled
-                    && !receiver_is_implemented(
-                        wfcx,
-                        receiver_trait_def_id,
-                        cause.clone(),
-                        potential_self_ty,
-                    )
-                {
-                    return false;
-                }
             }
-        } else {
-            debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
-            return false;
-        }
-    }
 
-    // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`.
-    if !arbitrary_self_types_enabled
-        && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty)
-    {
-        return false;
+            // Register the bound, in case it has any region side-effects.
+            wfcx.register_bound(
+                cause.clone(),
+                wfcx.param_env,
+                potential_self_ty,
+                receiver_trait_def_id,
+            );
+        }
     }
 
-    true
+    debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty);
+    false
 }
 
 fn receiver_is_implemented<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 61adb7a3cba..2ecb170ec89 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -17,7 +17,7 @@ use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::{Span, DUMMY_SP};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::traits::misc::{
     type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,
     ConstParamTyImplementationError, CopyImplementationError, InfringingFieldsReason,
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 5cb91603fd0..16f72f38d60 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -286,7 +286,7 @@ fn orphan_check<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl_def_id: LocalDefId,
     mode: OrphanCheckMode,
-) -> Result<(), OrphanCheckErr<'tcx, FxIndexSet<DefId>>> {
+) -> Result<(), OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>> {
     // We only accept this routine to be invoked on implementations
     // of a trait, not inherent implementations.
     let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
@@ -326,17 +326,16 @@ fn orphan_check<'tcx>(
             ty
         };
 
-        Ok(ty)
+        Ok::<_, !>(ty)
     };
 
-    let Ok(result) = traits::orphan_check_trait_ref::<!>(
+    let result = traits::orphan_check_trait_ref(
         &infcx,
         trait_ref,
         traits::InCrate::Local { mode },
         lazily_normalize_ty,
-    ) else {
-        unreachable!()
-    };
+    )
+    .into_ok();
 
     // (2)  Try to map the remaining inference vars back to generic params.
     result.map_err(|err| match err {
@@ -369,7 +368,7 @@ fn emit_orphan_check_error<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_ref: ty::TraitRef<'tcx>,
     impl_def_id: LocalDefId,
-    err: traits::OrphanCheckErr<'tcx, FxIndexSet<DefId>>,
+    err: traits::OrphanCheckErr<TyCtxt<'tcx>, FxIndexSet<DefId>>,
 ) -> ErrorGuaranteed {
     match err {
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
@@ -482,7 +481,7 @@ fn emit_orphan_check_error<'tcx>(
 
 fn lint_uncovered_ty_params<'tcx>(
     tcx: TyCtxt<'tcx>,
-    UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<'tcx, FxIndexSet<DefId>>,
+    UncoveredTyParams { uncovered, local_ty }: UncoveredTyParams<TyCtxt<'tcx>, FxIndexSet<DefId>>,
     impl_def_id: LocalDefId,
 ) {
     let hir_id = tcx.local_def_id_to_hir_id(impl_def_id);
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index e7892f17660..632ab3120d0 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -18,7 +18,9 @@ use rustc_ast::Recovered;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_data_structures::unord::UnordMap;
-use rustc_errors::{struct_span_code_err, Applicability, Diag, ErrorGuaranteed, StashKey, E0228};
+use rustc_errors::{
+    struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, StashKey, E0228,
+};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, walk_generics, Visitor};
@@ -35,8 +37,8 @@ use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::FieldIdx;
 use rustc_target::spec::abi;
+use rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
 use rustc_trait_selection::traits::ObligationCtxt;
 use std::cell::Cell;
 use std::iter;
@@ -70,10 +72,10 @@ pub fn provide(providers: &mut Providers) {
         predicates_of: predicates_of::predicates_of,
         predicates_defined_on,
         explicit_predicates_of: predicates_of::explicit_predicates_of,
-        super_predicates_of: predicates_of::super_predicates_of,
-        implied_predicates_of: predicates_of::implied_predicates_of,
-        super_predicates_that_define_assoc_item:
-            predicates_of::super_predicates_that_define_assoc_item,
+        explicit_super_predicates_of: predicates_of::explicit_super_predicates_of,
+        explicit_implied_predicates_of: predicates_of::explicit_implied_predicates_of,
+        explicit_supertraits_containing_assoc_item:
+            predicates_of::explicit_supertraits_containing_assoc_item,
         trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
         type_param_predicates: predicates_of::type_param_predicates,
         trait_def,
@@ -161,7 +163,7 @@ pub struct CollectItemTypesVisitor<'tcx> {
 /// and suggest adding type parameters in the appropriate place, taking into consideration any and
 /// all already existing generic type parameters to avoid suggesting a name that is already in use.
 pub(crate) fn placeholder_type_error<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    cx: &dyn HirTyLowerer<'tcx>,
     generics: Option<&hir::Generics<'_>>,
     placeholder_types: Vec<Span>,
     suggest: bool,
@@ -172,21 +174,21 @@ pub(crate) fn placeholder_type_error<'tcx>(
         return;
     }
 
-    placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
+    placeholder_type_error_diag(cx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
         .emit();
 }
 
-pub(crate) fn placeholder_type_error_diag<'tcx>(
-    tcx: TyCtxt<'tcx>,
+pub(crate) fn placeholder_type_error_diag<'cx, 'tcx>(
+    cx: &'cx dyn HirTyLowerer<'tcx>,
     generics: Option<&hir::Generics<'_>>,
     placeholder_types: Vec<Span>,
     additional_spans: Vec<Span>,
     suggest: bool,
     hir_ty: Option<&hir::Ty<'_>>,
     kind: &'static str,
-) -> Diag<'tcx> {
+) -> Diag<'cx> {
     if placeholder_types.is_empty() {
-        return bad_placeholder(tcx, additional_spans, kind);
+        return bad_placeholder(cx, additional_spans, kind);
     }
 
     let params = generics.map(|g| g.params).unwrap_or_default();
@@ -210,7 +212,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
     }
 
     let mut err =
-        bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
+        bad_placeholder(cx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
 
     // Suggest, but only if it is not a function in const or static
     if suggest {
@@ -224,7 +226,7 @@ pub(crate) fn placeholder_type_error_diag<'tcx>(
 
             // Check if parent is const or static
             is_const_or_static = matches!(
-                tcx.parent_hir_node(hir_ty.hir_id),
+                cx.tcx().parent_hir_node(hir_ty.hir_id),
                 Node::Item(&hir::Item {
                     kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
                     ..
@@ -267,7 +269,16 @@ fn reject_placeholder_type_signatures_in_item<'tcx>(
     let mut visitor = HirPlaceholderCollector::default();
     visitor.visit_item(item);
 
-    placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
+    let icx = ItemCtxt::new(tcx, item.owner_id.def_id);
+
+    placeholder_type_error(
+        icx.lowerer(),
+        Some(generics),
+        visitor.0,
+        suggest,
+        None,
+        item.kind.descr(),
+    );
 }
 
 impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
@@ -329,15 +340,15 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
 ///////////////////////////////////////////////////////////////////////////
 // Utility types and common code for the above passes.
 
-fn bad_placeholder<'tcx>(
-    tcx: TyCtxt<'tcx>,
+fn bad_placeholder<'cx, 'tcx>(
+    cx: &'cx dyn HirTyLowerer<'tcx>,
     mut spans: Vec<Span>,
     kind: &'static str,
-) -> Diag<'tcx> {
+) -> Diag<'cx> {
     let kind = if kind.ends_with('s') { format!("{kind}es") } else { format!("{kind}s") };
 
     spans.sort();
-    tcx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
+    cx.dcx().create_err(errors::PlaceholderNotAllowedItemSignatures { spans, kind })
 }
 
 impl<'tcx> ItemCtxt<'tcx> {
@@ -370,6 +381,10 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         self.tcx
     }
 
+    fn dcx(&self) -> DiagCtxtHandle<'_> {
+        self.tcx.dcx().taintable_handle(&self.tainted_by_errors)
+    }
+
     fn item_def_id(&self) -> LocalDefId {
         self.item_def_id
     }
@@ -377,14 +392,13 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
     fn re_infer(&self, span: Span, reason: RegionInferReason<'_>) -> ty::Region<'tcx> {
         if let RegionInferReason::BorrowedObjectLifetimeDefault = reason {
             let e = struct_span_code_err!(
-                self.tcx().dcx(),
+                self.dcx(),
                 span,
                 E0228,
                 "the lifetime bound for this object type cannot be deduced \
                 from context; please supply an explicit bound"
             )
             .emit();
-            self.set_tainted_by_errors(e);
             ty::Region::new_error(self.tcx(), e)
         } else {
             // This indicates an illegal lifetime in a non-assoc-trait position
@@ -509,10 +523,6 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
         None
     }
 
-    fn set_tainted_by_errors(&self, err: ErrorGuaranteed) {
-        self.tainted_by_errors.set(Some(err));
-    }
-
     fn lower_fn_sig(
         &self,
         decl: &hir::FnDecl<'tcx>,
@@ -570,7 +580,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
             // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
 
             let mut diag = crate::collect::placeholder_type_error_diag(
-                tcx,
+                self,
                 generics,
                 visitor.0,
                 infer_replacements.iter().map(|(s, _)| *s).collect(),
@@ -590,7 +600,7 @@ impl<'tcx> HirTyLowerer<'tcx> for ItemCtxt<'tcx> {
                 );
             }
 
-            self.set_tainted_by_errors(diag.emit());
+            diag.emit();
         }
 
         (input_tys, output_ty)
@@ -639,6 +649,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
     let it = tcx.hir().item(item_id);
     debug!(item = %it.ident, id = %it.hir_id());
     let def_id = item_id.owner_id.def_id;
+    let icx = ItemCtxt::new(tcx, def_id);
 
     match &it.kind {
         // These don't define types.
@@ -663,7 +674,7 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
                         let mut visitor = HirPlaceholderCollector::default();
                         visitor.visit_foreign_item(item);
                         placeholder_type_error(
-                            tcx,
+                            icx.lowerer(),
                             None,
                             visitor.0,
                             false,
@@ -691,14 +702,14 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
         hir::ItemKind::Trait(..) => {
             tcx.ensure().generics_of(def_id);
             tcx.ensure().trait_def(def_id);
-            tcx.at(it.span).super_predicates_of(def_id);
+            tcx.at(it.span).explicit_super_predicates_of(def_id);
             tcx.ensure().predicates_of(def_id);
             tcx.ensure().associated_items(def_id);
         }
         hir::ItemKind::TraitAlias(..) => {
             tcx.ensure().generics_of(def_id);
-            tcx.at(it.span).implied_predicates_of(def_id);
-            tcx.at(it.span).super_predicates_of(def_id);
+            tcx.at(it.span).explicit_implied_predicates_of(def_id);
+            tcx.at(it.span).explicit_super_predicates_of(def_id);
             tcx.ensure().predicates_of(def_id);
         }
         hir::ItemKind::Struct(struct_def, _) | hir::ItemKind::Union(struct_def, _) => {
@@ -742,7 +753,14 @@ fn lower_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
             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());
+                placeholder_type_error(
+                    icx.lowerer(),
+                    None,
+                    visitor.0,
+                    false,
+                    None,
+                    it.kind.descr(),
+                );
             }
         }
 
@@ -760,6 +778,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
     let trait_item = tcx.hir().trait_item(trait_item_id);
     let def_id = trait_item_id.owner_id;
     tcx.ensure().generics_of(def_id);
+    let icx = ItemCtxt::new(tcx, def_id.def_id);
 
     match trait_item.kind {
         hir::TraitItemKind::Fn(..) => {
@@ -776,7 +795,14 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
                 // Account for `const C: _;`.
                 let mut visitor = HirPlaceholderCollector::default();
                 visitor.visit_trait_item(trait_item);
-                placeholder_type_error(tcx, None, visitor.0, false, None, "associated constant");
+                placeholder_type_error(
+                    icx.lowerer(),
+                    None,
+                    visitor.0,
+                    false,
+                    None,
+                    "associated constant",
+                );
             }
         }
 
@@ -787,7 +813,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
             // Account for `type T = _;`.
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_trait_item(trait_item);
-            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+            placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
         }
 
         hir::TraitItemKind::Type(_, None) => {
@@ -798,7 +824,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_trait_item(trait_item);
 
-            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+            placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
         }
     };
 
@@ -811,6 +837,7 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
     tcx.ensure().type_of(def_id);
     tcx.ensure().predicates_of(def_id);
     let impl_item = tcx.hir().impl_item(impl_item_id);
+    let icx = ItemCtxt::new(tcx, def_id.def_id);
     match impl_item.kind {
         hir::ImplItemKind::Fn(..) => {
             tcx.ensure().codegen_fn_attrs(def_id);
@@ -821,14 +848,21 @@ fn lower_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_impl_item(impl_item);
 
-            placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+            placeholder_type_error(icx.lowerer(), None, visitor.0, false, None, "associated type");
         }
         hir::ImplItemKind::Const(ty, _) => {
             // Account for `const T: _ = ..;`
             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");
+                placeholder_type_error(
+                    icx.lowerer(),
+                    None,
+                    visitor.0,
+                    false,
+                    None,
+                    "associated constant",
+                );
             }
         }
     }
@@ -1194,6 +1228,11 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
         _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
     };
 
+    let constness = if tcx.has_attr(def_id, sym::const_trait) {
+        hir::Constness::Const
+    } else {
+        hir::Constness::NotConst
+    };
     let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
     if paren_sugar && !tcx.features().unboxed_closures {
         tcx.dcx().emit_err(errors::ParenSugarAttribute { span: item.span });
@@ -1201,6 +1240,7 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
 
     let is_marker = tcx.has_attr(def_id, sym::marker);
     let rustc_coinductive = tcx.has_attr(def_id, sym::rustc_coinductive);
+    let is_fundamental = tcx.has_attr(def_id, sym::fundamental);
 
     // FIXME: We could probably do way better attribute validation here.
     let mut skip_array_during_method_dispatch = false;
@@ -1348,10 +1388,12 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
     ty::TraitDef {
         def_id: def_id.to_def_id(),
         safety,
+        constness,
         paren_sugar,
         has_auto_impl: is_auto,
         is_marker,
         is_coinductive: rustc_coinductive || is_auto,
+        is_fundamental,
         skip_array_during_method_dispatch,
         skip_boxed_slice_during_method_dispatch,
         specialization_kind,
@@ -1377,7 +1419,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
             ..
         })
         | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
-            infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+            infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
         }
 
         ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
@@ -1394,7 +1436,7 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
                     None,
                 )
             } else {
-                infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+                infer_return_ty_for_fn_sig(sig, generics, def_id, &icx)
             }
         }
 
@@ -1447,27 +1489,44 @@ fn fn_sig(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_, ty::PolyFn
 }
 
 fn infer_return_ty_for_fn_sig<'tcx>(
-    tcx: TyCtxt<'tcx>,
     sig: &hir::FnSig<'tcx>,
     generics: &hir::Generics<'_>,
     def_id: LocalDefId,
     icx: &ItemCtxt<'tcx>,
 ) -> ty::PolyFnSig<'tcx> {
+    let tcx = icx.tcx;
     let hir_id = tcx.local_def_id_to_hir_id(def_id);
 
     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`.
+            // This is a heuristic approach. If the scope has region paramters,
+            // we should change fn_sig's lifetime from `ReErased` to `ReError`,
+            // otherwise to `ReStatic`.
+            let has_region_params = generics.params.iter().any(|param| match param.kind {
+                GenericParamKind::Lifetime { .. } => true,
+                _ => false,
+            });
             let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
-                ty::ReErased => tcx.lifetimes.re_static,
+                ty::ReErased => {
+                    if has_region_params {
+                        ty::Region::new_error_with_message(
+                            tcx,
+                            DUMMY_SP,
+                            "erased region is not allowed here in return type",
+                        )
+                    } else {
+                        tcx.lifetimes.re_static
+                    }
+                }
                 _ => r,
             });
 
             let mut visitor = HirPlaceholderCollector::default();
             visitor.visit_ty(ty);
 
-            let mut diag = bad_placeholder(tcx, visitor.0, "return type");
+            let mut diag = bad_placeholder(icx.lowerer(), visitor.0, "return type");
             let ret_ty = fn_sig.output();
             // Don't leak types into signatures unless they're nameable!
             // For example, if a function returns itself, we don't want that
@@ -1638,44 +1697,19 @@ fn impl_trait_header(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<ty::ImplTrai
     let icx = ItemCtxt::new(tcx, def_id);
     let item = tcx.hir().expect_item(def_id);
     let impl_ = item.expect_impl();
-    impl_
-        .of_trait
-        .as_ref()
-        .map(|hir_trait_ref| {
-            let self_ty = tcx.type_of(def_id).instantiate_identity();
+    impl_.of_trait.as_ref().map(|ast_trait_ref| {
+        let selfty = tcx.type_of(def_id).instantiate_identity();
 
-            let trait_ref = if let Some(ErrorGuaranteed { .. }) = check_impl_constness(
-                tcx,
-                tcx.is_const_trait_impl_raw(def_id.to_def_id()),
-                hir_trait_ref,
-            ) {
-                // we have a const impl, but for a trait without `#[const_trait]`, so
-                // without the host param. If we continue with the HIR trait ref, we get
-                // ICEs for generic arg count mismatch. We do a little HIR editing to
-                // make HIR ty lowering happy.
-                let mut path_segments = hir_trait_ref.path.segments.to_vec();
-                let last_segment = path_segments.len() - 1;
-                let mut args = *path_segments[last_segment].args();
-                let last_arg = args.args.len() - 1;
-                assert!(matches!(args.args[last_arg], hir::GenericArg::Const(anon_const) if anon_const.is_desugared_from_effects));
-                args.args = &args.args[..args.args.len() - 1];
-                path_segments[last_segment].args = Some(tcx.hir_arena.alloc(args));
-                let path = hir::Path {
-                    span: hir_trait_ref.path.span,
-                    res: hir_trait_ref.path.res,
-                    segments: tcx.hir_arena.alloc_slice(&path_segments),
-                };
-                let trait_ref = tcx.hir_arena.alloc(hir::TraitRef { path: tcx.hir_arena.alloc(path), hir_ref_id: hir_trait_ref.hir_ref_id });
-                icx.lowerer().lower_impl_trait_ref(trait_ref, self_ty)
-            } else {
-                icx.lowerer().lower_impl_trait_ref(hir_trait_ref, self_ty)
-            };
-            ty::ImplTraitHeader {
-                trait_ref: ty::EarlyBinder::bind(trait_ref),
-                safety: impl_.safety,
-                polarity: polarity_of_impl(tcx, def_id, impl_, item.span)
-            }
-        })
+        check_impl_constness(tcx, tcx.is_const_trait_impl_raw(def_id.to_def_id()), ast_trait_ref);
+
+        let trait_ref = icx.lowerer().lower_impl_trait_ref(ast_trait_ref, selfty);
+
+        ty::ImplTraitHeader {
+            trait_ref: ty::EarlyBinder::bind(trait_ref),
+            safety: impl_.safety,
+            polarity: polarity_of_impl(tcx, def_id, impl_, item.span),
+        }
+    })
 }
 
 fn check_impl_constness(
@@ -1688,7 +1722,7 @@ fn check_impl_constness(
     }
 
     let trait_def_id = hir_trait_ref.trait_def_id()?;
-    if tcx.has_attr(trait_def_id, sym::const_trait) {
+    if tcx.is_const_trait(trait_def_id) {
         return None;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs
index 85e1c600d6d..c73d3a5390d 100644
--- a/compiler/rustc_hir_analysis/src/collect/dump.rs
+++ b/compiler/rustc_hir_analysis/src/collect/dump.rs
@@ -1,5 +1,7 @@
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::CRATE_DEF_ID;
+use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
+use rustc_hir::intravisit;
+use rustc_middle::hir::nested_filter::OnlyBodies;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::sym;
 
@@ -41,3 +43,49 @@ pub(crate) fn predicates_and_item_bounds(tcx: TyCtxt<'_>) {
         }
     }
 }
+
+pub(crate) fn def_parents(tcx: TyCtxt<'_>) {
+    for did in tcx.hir().body_owners() {
+        if tcx.has_attr(did, sym::rustc_dump_def_parents) {
+            struct AnonConstFinder<'tcx> {
+                tcx: TyCtxt<'tcx>,
+                anon_consts: Vec<LocalDefId>,
+            }
+
+            impl<'tcx> intravisit::Visitor<'tcx> for AnonConstFinder<'tcx> {
+                type NestedFilter = OnlyBodies;
+
+                fn nested_visit_map(&mut self) -> Self::Map {
+                    self.tcx.hir()
+                }
+
+                fn visit_anon_const(&mut self, c: &'tcx rustc_hir::AnonConst) {
+                    self.anon_consts.push(c.def_id);
+                    intravisit::walk_anon_const(self, c)
+                }
+            }
+
+            // Look for any anon consts inside of this body owner as there is no way to apply
+            // the `rustc_dump_def_parents` attribute to the anon const so it would not be possible
+            // to see what its def parent is.
+            let mut anon_ct_finder = AnonConstFinder { tcx, anon_consts: vec![] };
+            intravisit::walk_expr(&mut anon_ct_finder, tcx.hir().body_owned_by(did).value);
+
+            for did in [did].into_iter().chain(anon_ct_finder.anon_consts) {
+                let span = tcx.def_span(did);
+
+                let mut diag = tcx.dcx().struct_span_err(
+                    span,
+                    format!("{}: {did:?}", sym::rustc_dump_def_parents.as_str()),
+                );
+
+                let mut current_did = did.to_def_id();
+                while let Some(parent_did) = tcx.opt_parent(current_did) {
+                    current_did = parent_did;
+                    diag.span_note(tcx.def_span(parent_did), format!("{parent_did:?}"));
+                }
+                diag.emit();
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 303fa23dbc1..22d465c8e62 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use crate::middle::resolve_bound_vars as rbv;
 use hir::{
     intravisit::{self, Visitor},
@@ -87,14 +89,9 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
             let mut in_param_ty = false;
             for (_parent, node) in tcx.hir().parent_iter(hir_id) {
                 if let Some(generics) = node.generics() {
-                    let mut visitor = AnonConstInParamTyDetector {
-                        in_param_ty: false,
-                        found_anon_const_in_param_ty: false,
-                        ct: hir_id,
-                    };
+                    let mut visitor = AnonConstInParamTyDetector { in_param_ty: false, ct: hir_id };
 
-                    visitor.visit_generics(generics);
-                    in_param_ty = visitor.found_anon_const_in_param_ty;
+                    in_param_ty = visitor.visit_generics(generics).is_break();
                     break;
                 }
             }
@@ -354,7 +351,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 kind,
             })
         }
-        GenericParamKind::Const { ty: _, default, is_host_effect } => {
+        GenericParamKind::Const { ty: _, default, is_host_effect, synthetic } => {
             if !matches!(allow_defaults, Defaults::Allowed)
                 && default.is_some()
                 // `host` effect params are allowed to have defaults.
@@ -388,6 +385,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
                 kind: ty::GenericParamDefKind::Const {
                     has_default: default.is_some(),
                     is_host_effect,
+                    synthetic,
                 },
             })
         }
@@ -459,50 +457,45 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
     struct LateBoundRegionsDetector<'tcx> {
         tcx: TyCtxt<'tcx>,
         outer_index: ty::DebruijnIndex,
-        has_late_bound_regions: Option<Span>,
     }
 
     impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> {
-        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
-            if self.has_late_bound_regions.is_some() {
-                return;
-            }
+        type Result = ControlFlow<Span>;
+        fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) -> ControlFlow<Span> {
             match ty.kind {
                 hir::TyKind::BareFn(..) => {
                     self.outer_index.shift_in(1);
-                    intravisit::walk_ty(self, ty);
+                    let res = intravisit::walk_ty(self, ty);
                     self.outer_index.shift_out(1);
+                    res
                 }
                 _ => intravisit::walk_ty(self, ty),
             }
         }
 
-        fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) {
-            if self.has_late_bound_regions.is_some() {
-                return;
-            }
+        fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) -> ControlFlow<Span> {
             self.outer_index.shift_in(1);
-            intravisit::walk_poly_trait_ref(self, tr);
+            let res = intravisit::walk_poly_trait_ref(self, tr);
             self.outer_index.shift_out(1);
+            res
         }
 
-        fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
-            if self.has_late_bound_regions.is_some() {
-                return;
-            }
-
+        fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) -> ControlFlow<Span> {
             match self.tcx.named_bound_var(lt.hir_id) {
-                Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {}
+                Some(rbv::ResolvedArg::StaticLifetime | rbv::ResolvedArg::EarlyBound(..)) => {
+                    ControlFlow::Continue(())
+                }
                 Some(rbv::ResolvedArg::LateBound(debruijn, _, _))
-                    if debruijn < self.outer_index => {}
+                    if debruijn < self.outer_index =>
+                {
+                    ControlFlow::Continue(())
+                }
                 Some(
                     rbv::ResolvedArg::LateBound(..)
                     | rbv::ResolvedArg::Free(..)
                     | rbv::ResolvedArg::Error(_),
                 )
-                | None => {
-                    self.has_late_bound_regions = Some(lt.ident.span);
-                }
+                | None => ControlFlow::Break(lt.ident.span),
             }
         }
     }
@@ -512,11 +505,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
         generics: &'tcx hir::Generics<'tcx>,
         decl: &'tcx hir::FnDecl<'tcx>,
     ) -> Option<Span> {
-        let mut visitor = LateBoundRegionsDetector {
-            tcx,
-            outer_index: ty::INNERMOST,
-            has_late_bound_regions: None,
-        };
+        let mut visitor = LateBoundRegionsDetector { tcx, outer_index: ty::INNERMOST };
         for param in generics.params {
             if let GenericParamKind::Lifetime { .. } = param.kind {
                 if tcx.is_late_bound(param.hir_id) {
@@ -524,8 +513,7 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
                 }
             }
         }
-        visitor.visit_fn_decl(decl);
-        visitor.has_late_bound_regions
+        visitor.visit_fn_decl(decl).break_value()
     }
 
     let decl = node.fn_decl()?;
@@ -535,25 +523,29 @@ fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<S
 
 struct AnonConstInParamTyDetector {
     in_param_ty: bool,
-    found_anon_const_in_param_ty: bool,
     ct: HirId,
 }
 
 impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
-    fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
-        if let GenericParamKind::Const { ty, default: _, is_host_effect: _ } = p.kind {
+    type Result = ControlFlow<()>;
+
+    fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) -> Self::Result {
+        if let GenericParamKind::Const { ty, default: _, is_host_effect: _, synthetic: _ } = p.kind
+        {
             let prev = self.in_param_ty;
             self.in_param_ty = true;
-            self.visit_ty(ty);
+            let res = self.visit_ty(ty);
             self.in_param_ty = prev;
+            res
+        } else {
+            ControlFlow::Continue(())
         }
     }
 
-    fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
+    fn visit_anon_const(&mut self, c: &'v hir::AnonConst) -> Self::Result {
         if self.in_param_ty && self.ct == c.hir_id {
-            self.found_anon_const_in_param_ty = true;
-        } else {
-            intravisit::walk_anon_const(self, c)
+            return ControlFlow::Break(());
         }
+        intravisit::walk_anon_const(self, c)
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index d084d3aefeb..c03e074c80b 100644
--- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -8,6 +8,7 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFold
 use rustc_middle::{bug, span_bug};
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::Span;
+use rustc_type_ir::Upcast;
 
 /// For associated types we include both bounds written on the type
 /// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`.
@@ -46,7 +47,7 @@ fn associated_type_bounds<'tcx>(
         }
     });
 
-    let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses().chain(bounds_from_parent));
+    let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent));
     debug!(
         "associated_type_bounds({}) = {:?}",
         tcx.def_path_str(assoc_item_def_id.to_def_id()),
@@ -75,7 +76,7 @@ fn opaque_type_bounds<'tcx>(
         icx.lowerer().add_sized_bound(&mut bounds, item_ty, hir_bounds, None, span);
         debug!(?bounds);
 
-        tcx.arena.alloc_from_iter(bounds.clauses())
+        tcx.arena.alloc_from_iter(bounds.clauses(tcx))
     })
 }
 
@@ -124,6 +125,32 @@ pub(super) fn explicit_item_bounds_with_filter(
         None => {}
     }
 
+    if tcx.is_effects_desugared_assoc_ty(def_id.to_def_id()) {
+        let mut predicates = Vec::new();
+
+        let parent = tcx.local_parent(def_id);
+
+        let preds = tcx.explicit_predicates_of(parent);
+
+        if let ty::AssocItemContainer::TraitContainer = tcx.associated_item(def_id).container {
+            // for traits, emit `type Effects: TyCompat<<(T1::Effects, ..) as Min>::Output>`
+            let tup = Ty::new(tcx, ty::Tuple(preds.effects_min_tys));
+            // FIXME(effects) span
+            let span = tcx.def_span(def_id);
+            let assoc = tcx.require_lang_item(hir::LangItem::EffectsIntersectionOutput, Some(span));
+            let proj = Ty::new_projection(tcx, assoc, [tup]);
+            let self_proj = Ty::new_projection(
+                tcx,
+                def_id.to_def_id(),
+                ty::GenericArgs::identity_for_item(tcx, def_id),
+            );
+            let trait_ = tcx.require_lang_item(hir::LangItem::EffectsTyCompat, Some(span));
+            let trait_ref = ty::TraitRef::new(tcx, trait_, [self_proj, proj]);
+            predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span));
+        }
+        return ty::EarlyBinder::bind(tcx.arena.alloc_from_iter(predicates));
+    }
+
     let bounds = match tcx.hir_node_by_def_id(def_id) {
         hir::Node::TraitItem(hir::TraitItem {
             kind: hir::TraitItemKind::Type(bounds, _),
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
index 3421c8da4e9..b89d034fc2e 100644
--- a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -57,6 +57,7 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
 #[instrument(level = "trace", skip(tcx), ret)]
 fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::GenericPredicates<'_> {
     use rustc_hir::*;
+    use rustc_middle::ty::Ty;
 
     match tcx.opt_rpitit_info(def_id.to_def_id()) {
         Some(ImplTraitInTraitData::Trait { fn_def_id, .. }) => {
@@ -84,6 +85,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             return ty::GenericPredicates {
                 parent: Some(tcx.parent(def_id.to_def_id())),
                 predicates: tcx.arena.alloc_from_iter(predicates),
+                effects_min_tys: ty::List::empty(),
             };
         }
 
@@ -105,6 +107,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
             return ty::GenericPredicates {
                 parent: Some(impl_def_id),
                 predicates: tcx.arena.alloc_from_iter(impl_predicates),
+                effects_min_tys: ty::List::empty(),
             };
         }
 
@@ -124,6 +127,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // We use an `IndexSet` to preserve order of insertion.
     // Preserving the order of insertion is important here so as not to break UI tests.
     let mut predicates: FxIndexSet<(ty::Clause<'_>, Span)> = FxIndexSet::default();
+    let mut effects_min_tys = Vec::new();
 
     let hir_generics = node.generics().unwrap_or(NO_GENERICS);
     if let Node::Item(item) = node {
@@ -150,11 +154,13 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // on a trait we must also consider the bounds that follow the trait's name,
     // like `trait Foo: A + B + C`.
     if let Some(self_bounds) = is_trait {
-        predicates.extend(
-            icx.lowerer()
-                .lower_mono_bounds(tcx.types.self_param, self_bounds, PredicateFilter::All)
-                .clauses(),
+        let bounds = icx.lowerer().lower_mono_bounds(
+            tcx.types.self_param,
+            self_bounds,
+            PredicateFilter::All,
         );
+        predicates.extend(bounds.clauses(tcx));
+        effects_min_tys.extend(bounds.effects_min_tys());
     }
 
     // In default impls, we can assume that the self type implements
@@ -187,7 +193,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     param.span,
                 );
                 trace!(?bounds);
-                predicates.extend(bounds.clauses());
+                predicates.extend(bounds.clauses(tcx));
                 trace!(?predicates);
             }
             hir::GenericParamKind::Const { .. } => {
@@ -238,7 +244,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
                     bound_vars,
                     OnlySelfBounds(false),
                 );
-                predicates.extend(bounds.clauses());
+                predicates.extend(bounds.clauses(tcx));
+                effects_min_tys.extend(bounds.effects_min_tys());
             }
 
             hir::WherePredicate::RegionPredicate(region_pred) => {
@@ -297,7 +304,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
     // and the duplicated parameter, to ensure that they do not get out of sync.
     if let Node::Item(&Item { kind: ItemKind::OpaqueTy(..), .. }) = node {
         let opaque_ty_node = tcx.parent_hir_node(hir_id);
-        let Node::Ty(&Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node else {
+        let Node::Ty(&hir::Ty { kind: TyKind::OpaqueDef(_, lifetimes, _), .. }) = opaque_ty_node
+        else {
             bug!("unexpected {opaque_ty_node:?}")
         };
         debug!(?lifetimes);
@@ -306,9 +314,30 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Gen
         debug!(?predicates);
     }
 
+    // add `Self::Effects: Compat<HOST>` to ensure non-const impls don't get called
+    // in const contexts.
+    if let Node::TraitItem(&TraitItem { kind: TraitItemKind::Fn(..), .. }) = node
+        && let Some(host_effect_index) = generics.host_effect_index
+    {
+        let parent = generics.parent.unwrap();
+        let Some(assoc_def_id) = tcx.associated_type_for_effects(parent) else {
+            bug!("associated_type_for_effects returned None when there is host effect in generics");
+        };
+        let effects =
+            Ty::new_projection(tcx, assoc_def_id, ty::GenericArgs::identity_for_item(tcx, parent));
+        let param = generics.param_at(host_effect_index, tcx);
+        let span = tcx.def_span(param.def_id);
+        let host = ty::Const::new_param(tcx, ty::ParamConst::for_def(param));
+        let compat = tcx.require_lang_item(LangItem::EffectsCompat, Some(span));
+        let trait_ref =
+            ty::TraitRef::new(tcx, compat, [ty::GenericArg::from(effects), host.into()]);
+        predicates.push((ty::Binder::dummy(trait_ref).upcast(tcx), span));
+    }
+
     ty::GenericPredicates {
         parent: generics.parent,
         predicates: tcx.arena.alloc_from_iter(predicates),
+        effects_min_tys: tcx.mk_type_list(&effects_min_tys),
     }
 }
 
@@ -459,6 +488,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             ty::GenericPredicates {
                 parent: predicates_and_bounds.parent,
                 predicates: tcx.arena.alloc_slice(&predicates),
+                effects_min_tys: predicates_and_bounds.effects_min_tys,
             }
         }
     } else {
@@ -510,6 +540,7 @@ pub(super) fn explicit_predicates_of<'tcx>(
             return GenericPredicates {
                 parent: parent_preds.parent,
                 predicates: { tcx.arena.alloc_from_iter(filtered_predicates) },
+                effects_min_tys: parent_preds.effects_min_tys,
             };
         }
         gather_explicit_predicates_of(tcx, def_id)
@@ -519,21 +550,21 @@ pub(super) fn explicit_predicates_of<'tcx>(
 /// Ensures that the super-predicates of the trait with a `DefId`
 /// of `trait_def_id` are lowered and stored. This also ensures that
 /// the transitive super-predicates are lowered.
-pub(super) fn super_predicates_of(
+pub(super) fn explicit_super_predicates_of(
     tcx: TyCtxt<'_>,
     trait_def_id: LocalDefId,
 ) -> ty::GenericPredicates<'_> {
     implied_predicates_with_filter(tcx, trait_def_id.to_def_id(), PredicateFilter::SelfOnly)
 }
 
-pub(super) fn super_predicates_that_define_assoc_item(
+pub(super) fn explicit_supertraits_containing_assoc_item(
     tcx: TyCtxt<'_>,
     (trait_def_id, assoc_name): (DefId, Ident),
 ) -> ty::GenericPredicates<'_> {
     implied_predicates_with_filter(tcx, trait_def_id, PredicateFilter::SelfThatDefines(assoc_name))
 }
 
-pub(super) fn implied_predicates_of(
+pub(super) fn explicit_implied_predicates_of(
     tcx: TyCtxt<'_>,
     trait_def_id: LocalDefId,
 ) -> ty::GenericPredicates<'_> {
@@ -560,7 +591,7 @@ pub(super) fn implied_predicates_with_filter(
         // if `assoc_name` is None, then the query should've been redirected to an
         // external provider
         assert!(matches!(filter, PredicateFilter::SelfThatDefines(_)));
-        return tcx.super_predicates_of(trait_def_id);
+        return tcx.explicit_super_predicates_of(trait_def_id);
     };
 
     let Node::Item(item) = tcx.hir_node_by_def_id(trait_def_id) else {
@@ -587,7 +618,7 @@ pub(super) fn implied_predicates_with_filter(
 
     // Combine the two lists to form the complete set of superbounds:
     let implied_bounds =
-        &*tcx.arena.alloc_from_iter(superbounds.clauses().chain(where_bounds_that_match));
+        &*tcx.arena.alloc_from_iter(superbounds.clauses(tcx).chain(where_bounds_that_match));
     debug!(?implied_bounds);
 
     // Now require that immediate supertraits are lowered, which will, in
@@ -601,7 +632,7 @@ pub(super) fn implied_predicates_with_filter(
                 if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder()
                     && bound.polarity == ty::PredicatePolarity::Positive
                 {
-                    tcx.at(span).super_predicates_of(bound.def_id());
+                    tcx.at(span).explicit_super_predicates_of(bound.def_id());
                 }
             }
         }
@@ -611,14 +642,18 @@ pub(super) fn implied_predicates_with_filter(
                 if let ty::ClauseKind::Trait(bound) = pred.kind().skip_binder()
                     && bound.polarity == ty::PredicatePolarity::Positive
                 {
-                    tcx.at(span).implied_predicates_of(bound.def_id());
+                    tcx.at(span).explicit_implied_predicates_of(bound.def_id());
                 }
             }
         }
         _ => {}
     }
 
-    ty::GenericPredicates { parent: None, predicates: implied_bounds }
+    ty::GenericPredicates {
+        parent: None,
+        predicates: implied_bounds,
+        effects_min_tys: ty::List::empty(),
+    }
 }
 
 /// Returns the predicates defined on `item_def_id` of the form
@@ -744,7 +779,7 @@ impl<'tcx> ItemCtxt<'tcx> {
             );
         }
 
-        bounds.clauses().collect()
+        bounds.clauses(self.tcx).collect()
     }
 
     #[instrument(level = "trace", skip(self))]
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 abc3bb838db..cad7870a0a1 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -951,7 +951,7 @@ impl<'a, 'tcx> Visitor<'tcx> for BoundVarContext<'a, 'tcx> {
                     self.visit_ty(ty);
                 }
             }
-            GenericParamKind::Const { ty, default, is_host_effect: _ } => {
+            GenericParamKind::Const { ty, default, .. } => {
                 self.visit_ty(ty);
                 if let Some(default) = default {
                     self.visit_body(self.tcx.hir().body(default.body));
@@ -1760,7 +1760,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             if let Some(assoc_item) = trait_defines_associated_item_named(def_id) {
                 break Some((bound_vars.into_iter().collect(), assoc_item));
             }
-            let predicates = tcx.super_predicates_that_define_assoc_item((def_id, assoc_name));
+            let predicates = tcx.explicit_supertraits_containing_assoc_item((def_id, assoc_name));
             let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
                 let bound_predicate = pred.kind();
                 match bound_predicate.skip_binder() {
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 1e2b0c43233..974dd415f46 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -12,6 +12,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::{Span, DUMMY_SP};
 
 use crate::errors::TypeofReservedKeywordUsed;
+use crate::hir_ty_lowering::HirTyLowerer;
 
 use super::bad_placeholder;
 use super::ItemCtxt;
@@ -357,7 +358,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
                 .and_then(|body_id| {
                     ty.is_suggestable_infer_ty().then(|| {
                         infer_placeholder_type(
-                            tcx,
+                            icx.lowerer(),
                             def_id,
                             body_id,
                             ty.span,
@@ -381,7 +382,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
             ImplItemKind::Const(ty, body_id) => {
                 if ty.is_suggestable_infer_ty() {
                     infer_placeholder_type(
-                        tcx,
+                        icx.lowerer(),
                         def_id,
                         body_id,
                         ty.span,
@@ -405,7 +406,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
             ItemKind::Static(ty, .., body_id) => {
                 if ty.is_suggestable_infer_ty() {
                     infer_placeholder_type(
-                        tcx,
+                        icx.lowerer(),
                         def_id,
                         body_id,
                         ty.span,
@@ -418,7 +419,14 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<'_
             }
             ItemKind::Const(ty, _, body_id) => {
                 if ty.is_suggestable_infer_ty() {
-                    infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant")
+                    infer_placeholder_type(
+                        icx.lowerer(),
+                        def_id,
+                        body_id,
+                        ty.span,
+                        item.ident,
+                        "constant",
+                    )
                 } else {
                     icx.lower_ty(ty)
                 }
@@ -559,21 +567,22 @@ pub(super) fn type_of_opaque(
     }
 }
 
-fn infer_placeholder_type<'a>(
-    tcx: TyCtxt<'a>,
+fn infer_placeholder_type<'tcx>(
+    cx: &dyn HirTyLowerer<'tcx>,
     def_id: LocalDefId,
     body_id: hir::BodyId,
     span: Span,
     item_ident: Ident,
     kind: &'static str,
-) -> Ty<'a> {
+) -> Ty<'tcx> {
+    let tcx = cx.tcx();
     let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id);
 
     // If this came from a free `const` or `static mut?` item,
     // then the user may have written e.g. `const A = 42;`.
     // In this case, the parser has stashed a diagnostic for
     // us to improve in typeck so we do that now.
-    let guar = tcx
+    let guar = cx
         .dcx()
         .try_steal_modify_and_emit_err(span, StashKey::ItemNoType, |err| {
             if !ty.references_error() {
@@ -602,7 +611,7 @@ fn infer_placeholder_type<'a>(
             }
         })
         .unwrap_or_else(|| {
-            let mut diag = bad_placeholder(tcx, vec![span], kind);
+            let mut diag = bad_placeholder(cx, vec![span], kind);
 
             if !ty.references_error() {
                 if let Some(ty) = ty.make_suggestable(tcx, false, None) {
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 44025c3cd61..0ee87a13e9e 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -9,6 +9,7 @@ use rustc_middle::ty::Ty;
 use rustc_span::{symbol::Ident, Span, Symbol};
 mod pattern_types;
 pub use pattern_types::*;
+pub mod wrong_number_of_generic_args;
 
 mod precise_captures;
 pub(crate) use precise_captures::*;
@@ -453,12 +454,11 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams {
             } else {
                 // The user wrote `Iterator`, so we don't have a type we can suggest, but at
                 // least we can clue them to the correct syntax `Iterator<Type>`.
-                err.span_suggestion(
-                    self.span,
+                err.span_suggestion_verbose(
+                    self.span.shrink_to_hi(),
                     fluent::hir_analysis_suggestion,
                     format!(
-                        "{}<{}>",
-                        snippet,
+                        "<{}>",
                         self.missing_type_params
                             .iter()
                             .map(|n| n.to_string())
@@ -694,29 +694,6 @@ pub(crate) struct TypeOf<'tcx> {
 }
 
 #[derive(Diagnostic)]
-#[diag(hir_analysis_pass_to_variadic_function, code = E0617)]
-pub(crate) struct PassToVariadicFunction<'tcx, 'a> {
-    #[primary_span]
-    pub span: Span,
-    pub ty: Ty<'tcx>,
-    pub cast_ty: &'a str,
-    #[suggestion(code = "{replace}", applicability = "machine-applicable")]
-    pub sugg_span: Option<Span>,
-    pub replace: String,
-    #[help]
-    pub help: Option<()>,
-}
-
-#[derive(Diagnostic)]
-#[diag(hir_analysis_cast_thin_pointer_to_fat_pointer, code = E0607)]
-pub(crate) struct CastThinPointerToFatPointer<'tcx> {
-    #[primary_span]
-    pub span: Span,
-    pub expr_ty: Ty<'tcx>,
-    pub cast_ty: String,
-}
-
-#[derive(Diagnostic)]
 #[diag(hir_analysis_invalid_union_field, code = E0740)]
 pub(crate) struct InvalidUnionField {
     #[primary_span]
@@ -1699,3 +1676,9 @@ pub struct InvalidReceiverTy<'tcx> {
     pub span: Span,
     pub receiver_ty: Ty<'tcx>,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_effects_without_next_solver)]
+#[note]
+#[help]
+pub struct EffectsWithoutNextSolver;
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
index 5d435a8edf9..6426ad9dc18 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs
@@ -1,8 +1,8 @@
-use crate::structured_errors::StructuredDiag;
-use rustc_errors::{codes::*, pluralize, Applicability, Diag, MultiSpan};
+use rustc_errors::{
+    codes::*, pluralize, Applicability, Diag, Diagnostic, EmissionGuarantee, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
-use rustc_session::Session;
 use rustc_span::def_id::DefId;
 use std::iter;
 
@@ -541,14 +541,8 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn start_diagnostics(&self) -> Diag<'tcx> {
-        let span = self.path_segment.ident.span;
-        let msg = self.create_error_message();
-        self.tcx.dcx().struct_span_err(span, msg).with_code(self.code())
-    }
-
     /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
-    fn notify(&self, err: &mut Diag<'_>) {
+    fn notify(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         let (quantifier, bound) = self.get_quantifier_and_bound();
         let provided_args = self.num_provided_args();
 
@@ -600,7 +594,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest(&self, err: &mut Diag<'_>) {
+    fn suggest(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         debug!(
             "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
             self.num_provided_args(),
@@ -628,7 +622,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String>;
     /// ```
-    fn suggest_adding_args(&self, err: &mut Diag<'_>) {
+    fn suggest_adding_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
             return;
         }
@@ -647,7 +641,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_>) {
+    fn suggest_adding_lifetime_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
         let num_missing_args = self.num_missing_lifetime_args();
         let num_params_to_take = num_missing_args;
@@ -701,7 +695,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_>) {
+    fn suggest_adding_type_and_const_args(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         let num_missing_args = self.num_missing_type_or_const_args();
         let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
 
@@ -761,7 +755,10 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```compile_fail
     /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
     /// ```
-    fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diag<'_>) {
+    fn suggest_moving_args_from_assoc_fn_to_trait(
+        &self,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
+    ) {
         let trait_ = match self.tcx.trait_of_item(self.def_id) {
             Some(def_id) => def_id,
             None => return,
@@ -817,7 +814,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
 
     fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
         &self,
-        err: &mut Diag<'_>,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
         qpath: &'tcx hir::QPath<'tcx>,
         msg: String,
         num_assoc_fn_excess_args: usize,
@@ -850,7 +847,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
 
     fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
         &self,
-        err: &mut Diag<'_>,
+        err: &mut Diag<'_, impl EmissionGuarantee>,
         trait_def_id: DefId,
         expr: &'tcx hir::Expr<'tcx>,
         msg: String,
@@ -904,7 +901,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String, String, String, String>;
     /// ```
-    fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_>) {
+    fn suggest_removing_args_or_generics(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         let num_provided_lt_args = self.num_provided_lifetime_args();
         let num_provided_type_const_args = self.num_provided_type_or_const_args();
         let unbound_types = self.get_unbound_associated_types();
@@ -922,7 +919,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         let provided_args_matches_unbound_traits =
             unbound_types.len() == num_redundant_type_or_const_args;
 
-        let remove_lifetime_args = |err: &mut Diag<'_>| {
+        let remove_lifetime_args = |err: &mut Diag<'_, _>| {
             let mut lt_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -963,7 +960,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             );
         };
 
-        let remove_type_or_const_args = |err: &mut Diag<'_>| {
+        let remove_type_or_const_args = |err: &mut Diag<'_, _>| {
             let mut gen_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -1060,7 +1057,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Builds the `type defined here` message.
-    fn show_definition(&self, err: &mut Diag<'_>) {
+    fn show_definition(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
             if self.tcx.sess.source_map().is_span_accessible(def_span) {
                 def_span.into()
@@ -1111,7 +1108,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Add note if `impl Trait` is explicitly specified.
-    fn note_synth_provided(&self, err: &mut Diag<'_>) {
+    fn note_synth_provided(&self, err: &mut Diag<'_, impl EmissionGuarantee>) {
         if !self.is_synth_provided() {
             return;
         }
@@ -1120,17 +1117,16 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 }
 
-impl<'tcx> StructuredDiag<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
-    fn session(&self) -> &Session {
-        self.tcx.sess
-    }
-
-    fn code(&self) -> ErrCode {
-        E0107
-    }
-
-    fn diagnostic_common(&self) -> Diag<'tcx> {
-        let mut err = self.start_diagnostics();
+impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for WrongNumberOfGenericArgs<'_, '_> {
+    fn into_diag(
+        self,
+        dcx: rustc_errors::DiagCtxtHandle<'a>,
+        level: rustc_errors::Level,
+    ) -> Diag<'a, G> {
+        let msg = self.create_error_message();
+        let mut err = Diag::new(dcx, level, msg);
+        err.code(E0107);
+        err.span(self.path_segment.ident.span);
 
         self.notify(&mut err);
         self.suggest(&mut err);
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
index 802215b2843..a1feef9e15b 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -76,7 +76,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
 
         if unbounds.len() > 1 {
-            tcx.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
+            self.dcx().emit_err(errors::MultipleRelaxedDefaultBounds {
                 spans: unbounds.iter().map(|ptr| ptr.span).collect(),
             });
         }
@@ -90,7 +90,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 continue;
             }
             // There was a `?Trait` bound, but it was not `?Sized`; warn.
-            tcx.dcx().span_warn(
+            self.dcx().span_warn(
                 unbound.span,
                 "relaxing a default bound only does something for `?Sized`; \
                 all other traits are not bound by default",
@@ -310,7 +310,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         duplicates
             .entry(assoc_item.def_id)
             .and_modify(|prev_span| {
-                tcx.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
+                self.dcx().emit_err(errors::ValueOfAssociatedStructAlreadySpecified {
                     span: constraint.span,
                     prev_span: *prev_span,
                     item_name: constraint.ident,
@@ -338,7 +338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         .into(),
                         ty::GenericParamDefKind::Type { .. } => {
                             let guar = *emitted_bad_param_err.get_or_insert_with(|| {
-                                tcx.dcx().emit_err(
+                                self.dcx().emit_err(
                                     crate::errors::ReturnTypeNotationIllegalParam::Type {
                                         span: path_span,
                                         param_span: tcx.def_span(param.def_id),
@@ -349,7 +349,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         }
                         ty::GenericParamDefKind::Const { .. } => {
                             let guar = *emitted_bad_param_err.get_or_insert_with(|| {
-                                tcx.dcx().emit_err(
+                                self.dcx().emit_err(
                                     crate::errors::ReturnTypeNotationIllegalParam::Const {
                                         span: path_span,
                                         param_span: tcx.def_span(param.def_id),
@@ -371,7 +371,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             {
                 alias_ty.into()
             } else {
-                return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
+                return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationOnNonRpitit {
                     span: constraint.span,
                     ty: tcx.liberate_late_bound_regions(assoc_item.def_id, output),
                     fn_span: tcx.hir().span_if_local(assoc_item.def_id),
@@ -417,7 +417,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let ty = alias_term
                     .map_bound(|alias| tcx.type_of(alias.def_id).instantiate(tcx, alias.args));
                 let ty =
-                    check_assoc_const_binding_type(tcx, constraint.ident, ty, constraint.hir_id);
+                    check_assoc_const_binding_type(self, constraint.ident, ty, constraint.hir_id);
                 tcx.feed_anon_const_type(anon_const.def_id, ty::EarlyBinder::bind(ty));
             }
 
@@ -426,7 +426,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         match constraint.kind {
             hir::AssocItemConstraintKind::Equality { .. } if let ty::AssocKind::Fn = assoc_kind => {
-                return Err(tcx.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
+                return Err(self.dcx().emit_err(crate::errors::ReturnTypeNotationEqualityBound {
                     span: constraint.span,
                 }));
             }
@@ -462,7 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     late_bound_in_term,
                     |br_name| {
                         struct_span_code_err!(
-                            tcx.dcx(),
+                            self.dcx(),
                             constraint.span,
                             E0582,
                             "binding for associated type `{}` references {}, \
@@ -519,7 +519,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 ///
 /// [^1]: <https://github.com/rust-lang/project-const-generics/issues/28>.
 fn check_assoc_const_binding_type<'tcx>(
-    tcx: TyCtxt<'tcx>,
+    cx: &dyn HirTyLowerer<'tcx>,
     assoc_const: Ident,
     ty: ty::Binder<'tcx, Ty<'tcx>>,
     hir_id: hir::HirId,
@@ -536,13 +536,14 @@ fn check_assoc_const_binding_type<'tcx>(
     }
 
     let mut collector = GenericParamAndBoundVarCollector {
-        tcx,
+        cx,
         params: Default::default(),
         vars: Default::default(),
         depth: ty::INNERMOST,
     };
     let mut guar = ty.visit_with(&mut collector).break_value();
 
+    let tcx = cx.tcx();
     let ty_note = ty
         .make_suggestable(tcx, false, None)
         .map(|ty| crate::errors::TyOfAssocConstBindingNote { assoc_const, ty });
@@ -556,7 +557,7 @@ fn check_assoc_const_binding_type<'tcx>(
     for index in collector.params {
         let param = generics.param_at(index as _, tcx);
         let is_self_param = param.name == rustc_span::symbol::kw::SelfUpper;
-        guar.get_or_insert(tcx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
+        guar.get_or_insert(cx.dcx().emit_err(crate::errors::ParamInTyOfAssocConstBinding {
             span: assoc_const.span,
             assoc_const,
             param_name: param.name,
@@ -574,7 +575,7 @@ fn check_assoc_const_binding_type<'tcx>(
         }));
     }
     for (var_def_id, var_name) in collector.vars {
-        guar.get_or_insert(tcx.dcx().emit_err(
+        guar.get_or_insert(cx.dcx().emit_err(
             crate::errors::EscapingBoundVarInTyOfAssocConstBinding {
                 span: assoc_const.span,
                 assoc_const,
@@ -590,14 +591,14 @@ fn check_assoc_const_binding_type<'tcx>(
     Ty::new_error(tcx, guar)
 }
 
-struct GenericParamAndBoundVarCollector<'tcx> {
-    tcx: TyCtxt<'tcx>,
+struct GenericParamAndBoundVarCollector<'a, 'tcx> {
+    cx: &'a dyn HirTyLowerer<'tcx>,
     params: FxIndexSet<u32>,
     vars: FxIndexSet<(DefId, Symbol)>,
     depth: ty::DebruijnIndex,
 }
 
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx> {
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'_, 'tcx> {
     type Result = ControlFlow<ErrorGuaranteed>;
 
     fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
@@ -620,7 +621,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx>
                     ty::BoundTyKind::Param(def_id, name) => (def_id, name),
                     ty::BoundTyKind::Anon => {
                         let reported = self
-                            .tcx
+                            .cx
                             .dcx()
                             .delayed_bug(format!("unexpected anon bound ty: {:?}", bt.var));
                         return ControlFlow::Break(reported);
@@ -643,7 +644,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx>
                     ty::BrNamed(def_id, name) => (def_id, name),
                     ty::BrAnon | ty::BrEnv => {
                         let guar = self
-                            .tcx
+                            .cx
                             .dcx()
                             .delayed_bug(format!("unexpected bound region kind: {:?}", br.kind));
                         return ControlFlow::Break(guar);
@@ -661,7 +662,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for GenericParamAndBoundVarCollector<'tcx>
                 self.params.insert(param.index);
             }
             ty::ConstKind::Bound(db, ty::BoundVar { .. }) if db >= self.depth => {
-                let guar = self.tcx.dcx().delayed_bug("unexpected escaping late-bound const var");
+                let guar = self.cx.dcx().delayed_bug("unexpected escaping late-bound const var");
                 return ControlFlow::Break(guar);
             }
             _ if ct.has_param() || ct.has_bound_vars() => return ct.super_visit_with(self),
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index 2d240699105..ebd2a63d555 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -4,7 +4,6 @@ use crate::errors::{
 };
 use crate::fluent_generated as fluent;
 use crate::hir_ty_lowering::HirTyLowerer;
-use crate::traits::error_reporting::report_object_safety_error;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
@@ -27,6 +26,7 @@ use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::BytePos;
 use rustc_span::{Span, Symbol, DUMMY_SP};
+use rustc_trait_selection::error_reporting::traits::report_object_safety_error;
 use rustc_trait_selection::traits::FulfillmentError;
 use rustc_trait_selection::traits::{
     object_safety_violations_for_assoc_item, TraitAliasExpansionInfo,
@@ -46,7 +46,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             return;
         }
 
-        self.tcx().dcx().emit_err(MissingTypeParams {
+        self.dcx().emit_err(MissingTypeParams {
             span,
             def_span: self.tcx().def_span(def_id),
             span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(),
@@ -109,7 +109,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         if is_impl {
             let trait_name = self.tcx().def_path_str(trait_def_id);
-            self.tcx().dcx().emit_err(ManualImplementation { span, trait_name });
+            self.dcx().emit_err(ManualImplementation { span, trait_name });
         }
     }
 
@@ -156,7 +156,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         if is_dummy {
             err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span });
-            return tcx.dcx().emit_err(err);
+            return self.dcx().emit_err(err);
         }
 
         let all_candidate_names: Vec<_> = all_candidates()
@@ -174,7 +174,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 assoc_kind: assoc_kind_str,
                 suggested_name,
             });
-            return tcx.dcx().emit_err(err);
+            return self.dcx().emit_err(err);
         }
 
         // If we didn't find a good item in the supertraits (or couldn't get
@@ -239,10 +239,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             assoc_kind: assoc_kind_str,
                             suggested_name,
                         });
-                        return tcx.dcx().emit_err(err);
+                        return self.dcx().emit_err(err);
                     }
 
-                    let mut err = tcx.dcx().create_err(err);
+                    let mut err = self.dcx().create_err(err);
                     if suggest_constraining_type_param(
                         tcx,
                         generics,
@@ -264,7 +264,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     }
                     return err.emit();
                 }
-                return tcx.dcx().emit_err(err);
+                return self.dcx().emit_err(err);
             }
         }
 
@@ -291,7 +291,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             err.label = Some(errors::AssocItemNotFoundLabel::NotFound { span: assoc_name.span });
         }
 
-        tcx.dcx().emit_err(err)
+        self.dcx().emit_err(err)
     }
 
     fn complain_about_assoc_kind_mismatch(
@@ -347,7 +347,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             (ident.span, None, assoc_kind, assoc_item.kind)
         };
 
-        tcx.dcx().emit_err(errors::AssocKindMismatch {
+        self.dcx().emit_err(errors::AssocKindMismatch {
             span,
             expected: super::assoc_kind_str(expected),
             got: super::assoc_kind_str(got),
@@ -366,8 +366,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         traits: &[String],
         name: Symbol,
     ) -> ErrorGuaranteed {
-        let mut err =
-            struct_span_code_err!(self.tcx().dcx(), span, E0223, "ambiguous associated type");
+        let mut err = struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type");
         if self
             .tcx()
             .resolutions(())
@@ -463,9 +462,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 }
             }
         }
-        let reported = err.emit();
-        self.set_tainted_by_errors(reported);
-        reported
+        err.emit()
     }
 
     pub(crate) fn complain_about_ambiguous_inherent_assoc_ty(
@@ -475,16 +472,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         span: Span,
     ) -> ErrorGuaranteed {
         let mut err = struct_span_code_err!(
-            self.tcx().dcx(),
+            self.dcx(),
             name.span,
             E0034,
             "multiple applicable items in scope"
         );
         err.span_label(name.span, format!("multiple `{name}` found"));
         self.note_ambiguous_inherent_assoc_ty(&mut err, candidates, span);
-        let reported = err.emit();
-        self.set_tainted_by_errors(reported);
-        reported
+        err.emit()
     }
 
     // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
@@ -576,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             };
 
             let mut err = struct_span_code_err!(
-                tcx.dcx(),
+                self.dcx(),
                 name.span,
                 E0220,
                 "associated type `{name}` not found for `{self_ty}` in the current scope"
@@ -662,7 +657,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         bounds.sort();
         bounds.dedup();
 
-        let mut err = tcx.dcx().struct_span_err(
+        let mut err = self.dcx().struct_span_err(
             name.span,
             format!("the associated type `{name}` exists for `{self_ty}`, but its trait bounds were not satisfied")
         );
@@ -829,7 +824,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         trait_bound_spans.sort();
         let mut err = struct_span_code_err!(
-            tcx.dcx(),
+            self.dcx(),
             trait_bound_spans,
             E0191,
             "the value of the associated type{} {} must be specified",
@@ -974,7 +969,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             }
         }
 
-        self.set_tainted_by_errors(err.emit());
+        err.emit();
     }
 
     /// On ambiguous associated type, look for an associated function whose name matches the
@@ -1011,17 +1006,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 .filter_by_name_unhygienic(Symbol::intern(&name))
                 .next()
         {
-            let reported =
-                struct_span_code_err!(tcx.dcx(), span, E0223, "ambiguous associated type")
-                    .with_span_suggestion_verbose(
-                        ident2.span.to(ident3.span),
-                        format!("there is an associated function with a similar name: `{name}`"),
-                        name,
-                        Applicability::MaybeIncorrect,
-                    )
-                    .emit();
-            self.set_tainted_by_errors(reported);
-            Err(reported)
+            Err(struct_span_code_err!(self.dcx(), span, E0223, "ambiguous associated type")
+                .with_span_suggestion_verbose(
+                    ident2.span.to(ident3.span),
+                    format!("there is an associated function with a similar name: `{name}`"),
+                    name,
+                    Applicability::MaybeIncorrect,
+                )
+                .emit())
         } else {
             Ok(())
         }
@@ -1120,7 +1112,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let last_span = *arg_spans.last().unwrap();
         let span: MultiSpan = arg_spans.into();
         let mut err = struct_span_code_err!(
-            self.tcx().dcx(),
+            self.dcx(),
             span,
             E0109,
             "{kind} arguments are not allowed on {this_type}",
@@ -1130,20 +1122,17 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             err.span_label(span, format!("not allowed on {what}"));
         }
         generics_args_err_extend(self.tcx(), segments, &mut err, err_extend);
-        let reported = err.emit();
-        self.set_tainted_by_errors(reported);
-        reported
+        err.emit()
     }
 
     pub fn report_trait_object_addition_traits_error(
         &self,
         regular_traits: &Vec<TraitAliasExpansionInfo<'_>>,
     ) -> ErrorGuaranteed {
-        let tcx = self.tcx();
         let first_trait = &regular_traits[0];
         let additional_trait = &regular_traits[1];
         let mut err = struct_span_code_err!(
-            tcx.dcx(),
+            self.dcx(),
             additional_trait.bottom().1,
             E0225,
             "only auto traits can be used as additional traits in a trait object"
@@ -1169,9 +1158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
              for more information on them, visit \
              <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
         );
-        let reported = err.emit();
-        self.set_tainted_by_errors(reported);
-        reported
+        err.emit()
     }
 
     pub fn report_trait_object_with_no_traits_error(
@@ -1185,20 +1172,19 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             .map(|&(trait_ref, _)| trait_ref.def_id())
             .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
             .map(|trait_ref| tcx.def_span(trait_ref));
-        let reported =
-            tcx.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
-        self.set_tainted_by_errors(reported);
-        reported
+
+        self.dcx().emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span })
     }
 }
 
 /// Emit an error for the given associated item constraint.
 pub fn prohibit_assoc_item_constraint(
-    tcx: TyCtxt<'_>,
+    cx: &dyn HirTyLowerer<'_>,
     constraint: &hir::AssocItemConstraint<'_>,
     segment: Option<(DefId, &hir::PathSegment<'_>, Span)>,
 ) -> ErrorGuaranteed {
-    let mut err = tcx.dcx().create_err(AssocItemConstraintsNotAllowedHere {
+    let tcx = cx.tcx();
+    let mut err = cx.dcx().create_err(AssocItemConstraintsNotAllowedHere {
         span: constraint.span,
         fn_trait_expansion: if let Some((_, segment, span)) = segment
             && segment.args().parenthesized == hir::GenericArgsParentheses::ParenSugar
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
index 3f888c4e272..b1c77db9f37 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/generics.rs
@@ -1,9 +1,9 @@
-use super::IsMethodCall;
+use super::{HirTyLowerer, IsMethodCall};
+use crate::errors::wrong_number_of_generic_args::{GenericArgsInfo, WrongNumberOfGenericArgs};
 use crate::hir_ty_lowering::{
     errors::prohibit_assoc_item_constraint, ExplicitLateBound, GenericArgCountMismatch,
     GenericArgCountResult, GenericArgPosition, GenericArgsLowerer,
 };
-use crate::structured_errors::{GenericArgsInfo, StructuredDiag, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{
     codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, MultiSpan,
@@ -13,7 +13,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::GenericArg;
 use rustc_middle::ty::{
-    self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
+    self, GenericArgsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty,
 };
 use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
 use rustc_span::symbol::{kw, sym};
@@ -22,15 +22,16 @@ use smallvec::SmallVec;
 /// Report an error that a generic argument did not match the generic parameter that was
 /// expected.
 fn generic_arg_mismatch_err(
-    tcx: TyCtxt<'_>,
+    cx: &dyn HirTyLowerer<'_>,
     arg: &GenericArg<'_>,
     param: &GenericParamDef,
     possible_ordering_error: bool,
     help: Option<String>,
 ) -> ErrorGuaranteed {
+    let tcx = cx.tcx();
     let sess = tcx.sess;
     let mut err = struct_span_code_err!(
-        tcx.dcx(),
+        cx.dcx(),
         arg.span(),
         E0747,
         "{} provided when a {} was expected",
@@ -171,7 +172,7 @@ fn generic_arg_mismatch_err(
 /// - `inferred_kind`: if no parameter was provided, and inference
 ///   is enabled, then creates a suitable inference variable.
 pub fn lower_generic_args<'tcx: 'a, 'a>(
-    tcx: TyCtxt<'tcx>,
+    cx: &dyn HirTyLowerer<'tcx>,
     def_id: DefId,
     parent_args: &[ty::GenericArg<'tcx>],
     has_self: bool,
@@ -179,6 +180,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
     arg_count: &GenericArgCountResult,
     ctx: &mut impl GenericArgsLowerer<'a, 'tcx>,
 ) -> GenericArgsRef<'tcx> {
+    let tcx = cx.tcx();
     // Collect the segments of the path; we need to instantiate arguments
     // for parameters throughout the entire path (wherever there are
     // generic parameters).
@@ -256,6 +258,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
                             | GenericParamDefKind::Lifetime,
                             _,
                         ) => {
+                            // FIXME(effects): this should be removed
                             // SPECIAL CASE FOR DESUGARED EFFECT PARAMS
                             // This comes from the following example:
                             //
@@ -325,7 +328,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
                                 param_types_present.dedup();
 
                                 generic_arg_mismatch_err(
-                                    tcx,
+                                    cx,
                                     arg,
                                     param,
                                     !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()),
@@ -380,7 +383,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
                         assert_eq!(kind, "lifetime");
                         let (provided_arg, param) =
                             force_infer_lt.expect("lifetimes ought to have been inferred");
-                        generic_arg_mismatch_err(tcx, provided_arg, param, false, None);
+                        generic_arg_mismatch_err(cx, provided_arg, param, false, None);
                     }
 
                     break;
@@ -404,7 +407,7 @@ pub fn lower_generic_args<'tcx: 'a, 'a>(
 /// Checks that the correct number of generic arguments have been provided.
 /// Used specifically for function calls.
 pub fn check_generic_arg_count_for_call(
-    tcx: TyCtxt<'_>,
+    cx: &dyn HirTyLowerer<'_>,
     def_id: DefId,
     generics: &ty::Generics,
     seg: &hir::PathSegment<'_>,
@@ -415,14 +418,14 @@ pub fn check_generic_arg_count_for_call(
         IsMethodCall::No => GenericArgPosition::Value,
     };
     let has_self = generics.parent.is_none() && generics.has_self;
-    check_generic_arg_count(tcx, def_id, seg, generics, gen_pos, has_self)
+    check_generic_arg_count(cx, def_id, seg, generics, gen_pos, has_self)
 }
 
 /// Checks that the correct number of generic arguments have been provided.
 /// This is used both for datatypes and function calls.
-#[instrument(skip(tcx, gen_pos), level = "debug")]
+#[instrument(skip(cx, gen_pos), level = "debug")]
 pub(crate) fn check_generic_arg_count(
-    tcx: TyCtxt<'_>,
+    cx: &dyn HirTyLowerer<'_>,
     def_id: DefId,
     seg: &hir::PathSegment<'_>,
     gen_params: &ty::Generics,
@@ -445,7 +448,7 @@ pub(crate) fn check_generic_arg_count(
         .own_params
         .iter()
         .filter(|param| {
-            matches!(param.kind, ty::GenericParamDefKind::Const { is_host_effect: true, .. })
+            matches!(param.kind, ty::GenericParamDefKind::Const { synthetic: true, .. })
         })
         .count();
     let named_const_param_count = param_counts.consts - synth_const_param_count;
@@ -455,11 +458,11 @@ pub(crate) fn check_generic_arg_count(
     if gen_pos != GenericArgPosition::Type
         && let Some(c) = gen_args.constraints.first()
     {
-        prohibit_assoc_item_constraint(tcx, c, None);
+        prohibit_assoc_item_constraint(cx, c, None);
     }
 
     let explicit_late_bound =
-        prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
+        prohibit_explicit_late_bound_lifetimes(cx, gen_params, gen_args, gen_pos);
 
     let mut invalid_args = vec![];
 
@@ -485,17 +488,15 @@ pub(crate) fn check_generic_arg_count(
             GenericArgsInfo::MissingLifetimes { num_missing_args }
         };
 
-        let reported = WrongNumberOfGenericArgs::new(
-            tcx,
+        let reported = cx.dcx().emit_err(WrongNumberOfGenericArgs::new(
+            cx.tcx(),
             gen_args_info,
             seg,
             gen_params,
             has_self as usize,
             gen_args,
             def_id,
-        )
-        .diagnostic()
-        .emit();
+        ));
 
         Err(reported)
     };
@@ -572,17 +573,17 @@ pub(crate) fn check_generic_arg_count(
         debug!(?gen_args_info);
 
         let reported = gen_args.has_err().unwrap_or_else(|| {
-            WrongNumberOfGenericArgs::new(
-                tcx,
-                gen_args_info,
-                seg,
-                gen_params,
-                params_offset,
-                gen_args,
-                def_id,
-            )
-            .diagnostic()
-            .emit_unless(all_params_are_binded)
+            cx.dcx()
+                .create_err(WrongNumberOfGenericArgs::new(
+                    cx.tcx(),
+                    gen_args_info,
+                    seg,
+                    gen_params,
+                    params_offset,
+                    gen_args,
+                    def_id,
+                ))
+                .emit_unless(all_params_are_binded)
         });
 
         Err(reported)
@@ -622,7 +623,7 @@ pub(crate) fn check_generic_arg_count(
 /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
 /// are present. This is used both for datatypes and function calls.
 pub(crate) fn prohibit_explicit_late_bound_lifetimes(
-    tcx: TyCtxt<'_>,
+    cx: &dyn HirTyLowerer<'_>,
     def: &ty::Generics,
     args: &hir::GenericArgs<'_>,
     position: GenericArgPosition,
@@ -643,13 +644,13 @@ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
         if position == GenericArgPosition::Value
             && args.num_lifetime_params() != param_counts.lifetimes
         {
-            struct_span_code_err!(tcx.dcx(), span, E0794, "{}", msg)
+            struct_span_code_err!(cx.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);
-            tcx.node_span_lint(
+            cx.tcx().node_span_lint(
                 LATE_BOUND_LIFETIME_ARGUMENTS,
                 args.args[0].hir_id(),
                 multispan,
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
index 240a749de96..29c71c3fa50 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
@@ -4,7 +4,7 @@ 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 rustc_trait_selection::error_reporting::traits::suggestions::NextTypeParamName;
 
 use super::HirTyLowerer;
 
@@ -60,7 +60,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             let msg = "trait objects must include the `dyn` keyword";
             let label = "add `dyn` keyword before this trait";
             let mut diag =
-                rustc_errors::struct_span_code_err!(tcx.dcx(), self_ty.span, E0782, "{}", msg);
+                rustc_errors::struct_span_code_err!(self.dcx(), self_ty.span, E0782, "{}", msg);
             if self_ty.span.can_be_used_for_suggestions()
                 && !self.maybe_suggest_impl_trait(self_ty, &mut diag)
             {
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
index 24ea328889c..a665306f2c6 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
@@ -28,7 +28,8 @@ use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, FatalError,
+    codes::*, struct_span_code_err, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed,
+    FatalError,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@@ -47,8 +48,9 @@ use rustc_middle::{bug, span_bug};
 use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS;
 use rustc_span::edit_distance::find_best_match_for_name;
 use rustc_span::symbol::{kw, Ident, Symbol};
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{Span, DUMMY_SP};
 use rustc_target::spec::abi;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::wf::object_region_bounds;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
@@ -101,6 +103,8 @@ pub enum RegionInferReason<'a> {
 pub trait HirTyLowerer<'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx>;
 
+    fn dcx(&self) -> DiagCtxtHandle<'_>;
+
     /// Returns the [`LocalDefId`] of the overarching item whose constituents get lowered.
     fn item_def_id(&self) -> LocalDefId;
 
@@ -176,12 +180,6 @@ pub trait HirTyLowerer<'tcx> {
     /// The inference context of the lowering context if applicable.
     fn infcx(&self) -> Option<&InferCtxt<'tcx>>;
 
-    /// Taint the context with errors.
-    ///
-    /// Invoke this when you encounter an error from some prior pass like name resolution.
-    /// This is used to help suppress derived errors typeck might otherwise report.
-    fn set_tainted_by_errors(&self, e: ErrorGuaranteed);
-
     /// Convenience method for coercing the lowering context into a trait object type.
     ///
     /// Most lowering routines are defined on the trait object type directly
@@ -322,7 +320,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             ty::BoundConstness::NotConst,
         );
         if let Some(c) = item_segment.args().constraints.first() {
-            prohibit_assoc_item_constraint(self.tcx(), c, Some((def_id, item_segment, span)));
+            prohibit_assoc_item_constraint(self, c, Some((def_id, item_segment, span)));
         }
         args
     }
@@ -393,7 +391,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
 
         let mut arg_count = check_generic_arg_count(
-            tcx,
+            self,
             def_id,
             segment,
             generics,
@@ -401,10 +399,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             self_ty.is_some(),
         );
 
-        if let Err(err) = &arg_count.correct {
-            self.set_tainted_by_errors(err.reported);
-        }
-
         // Skip processing if type has no generic parameters.
         // Traits always have `Self` as a generic parameter, which means they will not return early
         // here and so associated item constraints will be handled regardless of whether there are
@@ -559,13 +553,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         }
         if let ty::BoundConstness::Const | ty::BoundConstness::ConstIfConst = constness
             && generics.has_self
-            && !tcx.has_attr(def_id, sym::const_trait)
+            && !tcx.is_const_trait(def_id)
         {
-            let reported = tcx.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
+            let reported = self.dcx().emit_err(crate::errors::ConstBoundForNonConstTrait {
                 span,
                 modifier: constness.as_str(),
             });
-            self.set_tainted_by_errors(reported);
             arg_count.correct = Err(GenericArgCountMismatch { reported, invalid_args: vec![] });
         }
 
@@ -578,7 +571,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             incorrect_args: &arg_count.correct,
         };
         let args = lower_generic_args(
-            tcx,
+            self,
             def_id,
             parent_args,
             self_ty.is_some(),
@@ -608,7 +601,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             ty::BoundConstness::NotConst,
         );
         if let Some(c) = item_segment.args().constraints.first() {
-            prohibit_assoc_item_constraint(self.tcx(), c, Some((item_def_id, item_segment, span)));
+            prohibit_assoc_item_constraint(self, c, Some((item_def_id, item_segment, span)));
         }
         args
     }
@@ -698,7 +691,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         );
 
         debug!(?poly_trait_ref);
-        bounds.push_trait_bound(tcx, poly_trait_ref, span, polarity);
+        bounds.push_trait_bound(
+            tcx,
+            self.item_def_id().to_def_id(),
+            poly_trait_ref,
+            span,
+            polarity,
+            constness,
+        );
 
         let mut dup_constraints = FxIndexMap::default();
         for constraint in trait_segment.args().constraints {
@@ -707,7 +707,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             // would not be well-formed!
             if polarity != ty::PredicatePolarity::Positive {
                 assert!(
-                    self.tcx().dcx().has_errors().is_some(),
+                    self.dcx().has_errors().is_some(),
                     "negative trait bounds should not have assoc item constraints",
                 );
                 continue;
@@ -753,11 +753,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             constness,
         );
         if let Some(c) = trait_segment.args().constraints.first() {
-            prohibit_assoc_item_constraint(
-                self.tcx(),
-                c,
-                Some((trait_def_id, trait_segment, span)),
-            );
+            prohibit_assoc_item_constraint(self, c, Some((trait_def_id, trait_segment, span)));
         }
         ty::TraitRef::new_from_args(self.tcx(), trait_def_id, generic_args)
     }
@@ -869,7 +865,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 span,
                 constraint,
             );
-            self.set_tainted_by_errors(reported);
             return Err(reported);
         };
         debug!(?bound);
@@ -879,7 +874,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
             let assoc_kind_str = assoc_kind_str(assoc_kind);
             let ty_param_name = &ty_param_name.to_string();
-            let mut err = tcx.dcx().create_err(crate::errors::AmbiguousAssocItem {
+            let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem {
                 span,
                 assoc_kind: assoc_kind_str,
                 assoc_name,
@@ -952,7 +947,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 ));
             }
             let reported = err.emit();
-            self.set_tainted_by_errors(reported);
             if !where_bounds.is_empty() {
                 return Err(reported);
             }
@@ -1051,7 +1045,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 // trait reference.
                 let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
                     // A cycle error occurred, most likely.
-                    tcx.dcx().span_bug(span, "expected cycle error");
+                    self.dcx().span_bug(span, "expected cycle error");
                 };
 
                 self.probe_single_bound_for_assoc_item(
@@ -1081,10 +1075,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let reported = if variant_resolution.is_some() {
                     // Variant in type position
                     let msg = format!("expected type, found variant `{assoc_ident}`");
-                    tcx.dcx().span_err(span, msg)
+                    self.dcx().span_err(span, msg)
                 } else if qself_ty.is_enum() {
                     let mut err = struct_span_code_err!(
-                        tcx.dcx(),
+                        self.dcx(),
                         assoc_ident.span,
                         E0599,
                         "no variant named `{}` found for enum `{}`",
@@ -1125,7 +1119,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 } else if let ty::Alias(ty::Opaque, alias_ty) = qself_ty.kind() {
                     // `<impl Trait as OtherTrait>::Assoc` makes no sense.
                     struct_span_code_err!(
-                        tcx.dcx(),
+                        self.dcx(),
                         tcx.def_span(alias_ty.def_id),
                         E0667,
                         "`impl Trait` is not allowed in path parameters"
@@ -1145,7 +1139,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         assoc_ident.name,
                     )
                 };
-                self.set_tainted_by_errors(reported);
                 return Err(reported);
             }
         };
@@ -1396,13 +1389,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let tcx = self.tcx();
 
         if !tcx.visibility(item_def_id).is_accessible_from(scope, tcx) {
-            let reported = tcx.dcx().emit_err(crate::errors::AssocItemIsPrivate {
+            self.dcx().emit_err(crate::errors::AssocItemIsPrivate {
                 span,
                 kind: tcx.def_descr(item_def_id),
                 name: ident,
                 defined_here_label: tcx.def_span(item_def_id),
             });
-            self.set_tainted_by_errors(reported);
         }
 
         tcx.check_stability(item_def_id, Some(block), span, None);
@@ -1556,7 +1548,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         for segment in segments {
             // Only emit the first error to avoid overloading the user with error messages.
             if let Some(c) = segment.args().constraints.first() {
-                return Err(prohibit_assoc_item_constraint(self.tcx(), c, None));
+                return Err(prohibit_assoc_item_constraint(self, c, None));
             }
         }
 
@@ -1816,7 +1808,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 // `AlwaysApplicable` impl needs a `T: ?Sized` bound for
                 // this to compile if we were to normalize here.
                 if forbid_generic && ty.has_param() {
-                    let mut err = tcx.dcx().struct_span_err(
+                    let mut err = self.dcx().struct_span_err(
                         path.span,
                         "generic `Self` types are currently not permitted in anonymous constants",
                     );
@@ -1828,7 +1820,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         err.span_note(impl_.self_ty.span, "not a concrete type");
                     }
                     let reported = err.emit();
-                    self.set_tainted_by_errors(reported);
                     Ty::new_error(tcx, reported)
                 } else {
                     ty
@@ -1840,19 +1831,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     path.segments[..path.segments.len() - 2].iter(),
                     GenericsArgsErrExtend::None,
                 );
-                // HACK: until we support `<Type as ~const Trait>`, assume all of them are.
-                let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
-                    ty::BoundConstness::ConstIfConst
-                } else {
-                    ty::BoundConstness::NotConst
-                };
                 self.lower_qpath(
                     span,
                     opt_self_ty,
                     def_id,
                     &path.segments[path.segments.len() - 2],
                     path.segments.last().unwrap(),
-                    constness,
+                    ty::BoundConstness::NotConst,
                 )
             }
             Res::PrimTy(prim_ty) => {
@@ -1875,7 +1860,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                     .tcx()
                     .dcx()
                     .span_delayed_bug(path.span, "path with `Res::Err` but no error emitted");
-                self.set_tainted_by_errors(e);
                 Ty::new_error(self.tcx(), e)
             }
             Res::Def(..) => {
@@ -1886,7 +1870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 );
                 Ty::new_error(
                     self.tcx(),
-                    self.tcx().dcx().span_delayed_bug(span, "incorrect resolution for `Self`"),
+                    self.dcx().span_delayed_bug(span, "incorrect resolution for `Self`"),
                 )
             }
             _ => span_bug!(span, "unexpected resolution: {:?}", path.res),
@@ -1959,7 +1943,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         let sig_span = self.tcx().def_span(sig_id);
         let mut try_emit = |descr| {
             if emit {
-                self.tcx().dcx().emit_err(crate::errors::NotSupportedDelegation {
+                self.dcx().emit_err(crate::errors::NotSupportedDelegation {
                     span,
                     descr,
                     callee_span: sig_span,
@@ -2009,8 +1993,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     ) -> Ty<'tcx> {
         if self.check_delegation_constraints(sig_id, span, idx == hir::InferDelegationKind::Output)
         {
-            let e = self.tcx().dcx().span_delayed_bug(span, "not supported delegation case");
-            self.set_tainted_by_errors(e);
+            let e = self.dcx().span_delayed_bug(span, "not supported delegation case");
             return Ty::new_error(self.tcx(), e);
         };
         let sig = self.tcx().fn_sig(sig_id);
@@ -2175,7 +2158,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 let ty = self.lower_ty(ty);
                 let pat_ty = match pat.kind {
                     hir::PatKind::Wild => {
-                        let err = tcx.dcx().emit_err(WildPatTy { span: pat.span });
+                        let err = self.dcx().emit_err(WildPatTy { span: pat.span });
                         Ty::new_error(tcx, err)
                     }
                     hir::PatKind::Range(start, end, include_end) => {
@@ -2355,7 +2338,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
             struct_span_code_err!(
-                tcx.dcx(),
+                self.dcx(),
                 decl.output.span(),
                 E0581,
                 "return type references {}, which is not constrained by the fn input types",
@@ -2406,11 +2389,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
     }
 
     #[instrument(level = "trace", skip(self, generate_err))]
-    fn validate_late_bound_regions(
-        &self,
+    fn validate_late_bound_regions<'cx>(
+        &'cx self,
         constrained_regions: FxHashSet<ty::BoundRegionKind>,
         referenced_regions: FxHashSet<ty::BoundRegionKind>,
-        generate_err: impl Fn(&str) -> Diag<'tcx>,
+        generate_err: impl Fn(&str) -> Diag<'cx>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
             let br_name = match *br {
@@ -2435,7 +2418,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                 err.note("consider introducing a named lifetime parameter");
             }
 
-            self.set_tainted_by_errors(err.emit());
+            err.emit();
         }
     }
 
@@ -2476,7 +2459,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
         // error.
         let r = derived_region_bounds[0];
         if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
-            self.set_tainted_by_errors(tcx.dcx().emit_err(AmbiguousLifetimeBound { span }));
+            self.dcx().emit_err(AmbiguousLifetimeBound { span });
         }
         Some(r)
     }
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
index 34924f09d09..aafadc7f9cb 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/object_safety.rs
@@ -13,7 +13,7 @@ use rustc_middle::ty::fold::BottomUpFolder;
 use rustc_middle::ty::{self, ExistentialPredicateStableCmpExt as _, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{DynKind, Upcast};
 use rustc_span::{ErrorGuaranteed, Span};
-use rustc_trait_selection::traits::error_reporting::report_object_safety_error;
+use rustc_trait_selection::error_reporting::traits::report_object_safety_error;
 use rustc_trait_selection::traits::{self, hir_ty_lowering_object_safety_violations};
 
 use smallvec::{smallvec, SmallVec};
@@ -59,7 +59,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
         let mut trait_bounds = vec![];
         let mut projection_bounds = vec![];
-        for (pred, span) in bounds.clauses() {
+        for (pred, span) in bounds.clauses(tcx) {
             let bound_pred = pred.kind();
             match bound_pred.skip_binder() {
                 ty::ClauseKind::Trait(trait_pred) => {
@@ -133,7 +133,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             tcx.associated_items(pred.def_id())
                                 .in_definition_order()
                                 .filter(|item| item.kind == ty::AssocKind::Type)
-                                .filter(|item| !item.is_impl_trait_in_trait())
+                                .filter(|item| {
+                                    !item.is_impl_trait_in_trait() && !item.is_effects_desugaring
+                                })
                                 .map(|item| item.def_id),
                         );
                     }
@@ -234,7 +236,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             Ty::new_misc_error(tcx).into()
                         } else if arg.walk().any(|arg| arg == dummy_self.into()) {
                             references_self = true;
-                            let guar = tcx.dcx().span_delayed_bug(
+                            let guar = self.dcx().span_delayed_bug(
                                 span,
                                 "trait object trait bounds reference `Self`",
                             );
@@ -260,8 +262,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
 
                 if references_self {
                     let def_id = i.bottom().0.def_id();
-                    let reported = struct_span_code_err!(
-                        tcx.dcx(),
+                    struct_span_code_err!(
+                        self.dcx(),
                         i.bottom().1,
                         E0038,
                         "the {} `{}` cannot be made into an object",
@@ -273,7 +275,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                             .error_msg(),
                     )
                     .emit();
-                    self.set_tainted_by_errors(reported);
                 }
 
                 ty::ExistentialTraitRef { def_id: trait_ref.def_id, args }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index f3ce3ab6655..5b8b6e98125 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -78,7 +78,7 @@ use rustc_middle::ty::trait_def::TraitSpecializationKind;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
 use rustc_middle::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use rustc_span::{ErrorGuaranteed, Span};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
 use rustc_trait_selection::traits::{self, translate_args_with_cause, wf, ObligationCtxt};
 
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 0428abcdf24..dd7fbba753b 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -71,6 +71,7 @@ This API is completely unstable and subject to change.
 #![feature(rustdoc_internals)]
 #![feature(slice_partition_dedup)]
 #![feature(try_blocks)]
+#![feature(unwrap_infallible)]
 // tidy-alphabetical-end
 
 #[macro_use]
@@ -91,7 +92,6 @@ mod errors;
 pub mod hir_wf_check;
 mod impl_wf_check;
 mod outlives;
-pub mod structured_errors;
 mod variance;
 
 use rustc_hir as hir;
@@ -151,6 +151,12 @@ pub fn provide(providers: &mut Providers) {
 pub fn check_crate(tcx: TyCtxt<'_>) {
     let _prof_timer = tcx.sess.timer("type_check_crate");
 
+    // FIXME(effects): remove once effects is implemented in old trait solver
+    // or if the next solver is stabilized.
+    if tcx.features().effects && !tcx.next_trait_solver_globally() {
+        tcx.dcx().emit_err(errors::EffectsWithoutNextSolver);
+    }
+
     tcx.sess.time("coherence_checking", || {
         tcx.hir().par_for_each_module(|module| {
             let _ = tcx.ensure().check_mod_type_wf(module);
@@ -169,6 +175,7 @@ pub fn check_crate(tcx: TyCtxt<'_>) {
         tcx.sess.time("variance_dumping", || variance::dump::variances(tcx));
         collect::dump::opaque_hidden_types(tcx);
         collect::dump::predicates_and_item_bounds(tcx);
+        collect::dump::def_parents(tcx);
     }
 
     // Make sure we evaluate all static and (non-associated) const items, even if unused.
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
index 5086c2af3f6..08015c28a26 100644
--- a/compiler/rustc_hir_analysis/src/outlives/utils.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -1,9 +1,9 @@
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_middle::ty::{self, Region, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArg, GenericArgKind};
 use rustc_middle::{bug, span_bug};
 use rustc_span::Span;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 /// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
diff --git a/compiler/rustc_hir_analysis/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs
deleted file mode 100644
index 5abfd25dd95..00000000000
--- a/compiler/rustc_hir_analysis/src/structured_errors.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-mod missing_cast_for_variadic_arg;
-mod sized_unsized_cast;
-mod wrong_number_of_generic_args;
-
-pub use self::{
-    missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*,
-};
-
-use rustc_errors::{Diag, ErrCode};
-use rustc_session::Session;
-
-pub trait StructuredDiag<'tcx> {
-    fn session(&self) -> &Session;
-
-    fn code(&self) -> ErrCode;
-
-    fn diagnostic(&self) -> Diag<'tcx> {
-        let err = self.diagnostic_common();
-
-        if self.session().teach(self.code()) {
-            self.diagnostic_extended(err)
-        } else {
-            self.diagnostic_regular(err)
-        }
-    }
-
-    fn diagnostic_common(&self) -> Diag<'tcx>;
-
-    fn diagnostic_regular(&self, err: Diag<'tcx>) -> Diag<'tcx> {
-        err
-    }
-
-    fn diagnostic_extended(&self, err: Diag<'tcx>) -> Diag<'tcx> {
-        err
-    }
-}
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
deleted file mode 100644
index 0e78acbeae2..00000000000
--- a/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use crate::{errors, structured_errors::StructuredDiag};
-use rustc_errors::{codes::*, Diag};
-use rustc_middle::ty::{Ty, TypeVisitableExt};
-use rustc_session::Session;
-use rustc_span::Span;
-
-pub struct MissingCastForVariadicArg<'tcx, 's> {
-    pub sess: &'tcx Session,
-    pub span: Span,
-    pub ty: Ty<'tcx>,
-    pub cast_ty: &'s str,
-}
-
-impl<'tcx> StructuredDiag<'tcx> for MissingCastForVariadicArg<'tcx, '_> {
-    fn session(&self) -> &Session {
-        self.sess
-    }
-
-    fn code(&self) -> ErrCode {
-        E0617
-    }
-
-    fn diagnostic_common(&self) -> Diag<'tcx> {
-        let (sugg_span, replace, help) =
-            if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) {
-                (Some(self.span), format!("{} as {}", snippet, self.cast_ty), None)
-            } else {
-                (None, "".to_string(), Some(()))
-            };
-
-        let mut err = self.sess.dcx().create_err(errors::PassToVariadicFunction {
-            span: self.span,
-            ty: self.ty,
-            cast_ty: self.cast_ty,
-            help,
-            replace,
-            sugg_span,
-        });
-
-        if self.ty.references_error() {
-            err.downgrade_to_delayed_bug();
-        }
-
-        err
-    }
-
-    fn diagnostic_extended(&self, mut err: Diag<'tcx>) -> Diag<'tcx> {
-        err.note(format!(
-            "certain types, like `{}`, must be casted before passing them to a \
-                variadic function, because of arcane ABI rules dictated by the C \
-                standard",
-            self.ty
-        ));
-
-        err
-    }
-}
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
deleted file mode 100644
index 9e871ff9af0..00000000000
--- a/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use crate::{errors, structured_errors::StructuredDiag};
-use rustc_errors::{codes::*, Diag};
-use rustc_middle::ty::{Ty, TypeVisitableExt};
-use rustc_session::Session;
-use rustc_span::Span;
-
-pub struct SizedUnsizedCast<'tcx> {
-    pub sess: &'tcx Session,
-    pub span: Span,
-    pub expr_ty: Ty<'tcx>,
-    pub cast_ty: String,
-}
-
-impl<'tcx> StructuredDiag<'tcx> for SizedUnsizedCast<'tcx> {
-    fn session(&self) -> &Session {
-        self.sess
-    }
-
-    fn code(&self) -> ErrCode {
-        E0607
-    }
-
-    fn diagnostic_common(&self) -> Diag<'tcx> {
-        let mut err = self.sess.dcx().create_err(errors::CastThinPointerToFatPointer {
-            span: self.span,
-            expr_ty: self.expr_ty,
-            cast_ty: self.cast_ty.to_owned(),
-        });
-
-        if self.expr_ty.references_error() {
-            err.downgrade_to_delayed_bug();
-        }
-
-        err
-    }
-
-    fn diagnostic_extended(&self, mut err: Diag<'tcx>) -> Diag<'tcx> {
-        err.help(
-            "Thin pointers are \"simple\" pointers: they are purely a reference to a
-memory address.
-
-Fat pointers are pointers referencing \"Dynamically Sized Types\" (also
-called DST). DST don't have a statically known size, therefore they can
-only exist behind some kind of pointers that contain additional
-information. Slices and trait objects are DSTs. In the case of slices,
-the additional information the fat pointer holds is their size.
-
-To fix this error, don't try to cast directly between thin and fat
-pointers.
-
-For more information about casts, take a look at The Book:
-https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions",
-        );
-        err
-    }
-}
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 25b0cbdc026..5105d60ae18 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -585,6 +585,7 @@ impl<'a> State<'a> {
                 self.print_struct(struct_def, generics, item.ident.name, item.span, true);
             }
             hir::ItemKind::Impl(&hir::Impl {
+                constness,
                 safety,
                 polarity,
                 defaultness,
@@ -599,6 +600,10 @@ impl<'a> State<'a> {
                 self.print_safety(safety);
                 self.word_nbsp("impl");
 
+                if let hir::Constness::Const = constness {
+                    self.word_nbsp("const");
+                }
+
                 if !generics.params.is_empty() {
                     self.print_generic_params(generics.params);
                     self.space();
@@ -2144,7 +2149,7 @@ impl<'a> State<'a> {
                     self.print_type(default);
                 }
             }
-            GenericParamKind::Const { ty, ref default, is_host_effect: _ } => {
+            GenericParamKind::Const { ty, ref default, is_host_effect: _, synthetic: _ } => {
                 self.word_space(":");
                 self.print_type(ty);
                 if let Some(default) = default {
diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl
index d6f3f4d640b..39d430cf73b 100644
--- a/compiler/rustc_hir_typeck/messages.ftl
+++ b/compiler/rustc_hir_typeck/messages.ftl
@@ -23,6 +23,22 @@ hir_typeck_cannot_cast_to_bool = cannot cast `{$expr_ty}` as `bool`
 
 hir_typeck_cast_enum_drop = cannot cast enum `{$expr_ty}` into integer `{$cast_ty}` because it implements `Drop`
 
+hir_typeck_cast_thin_pointer_to_fat_pointer = cannot cast thin pointer `{$expr_ty}` to fat pointer `{$cast_ty}`
+    .teach_help = Thin pointers are "simple" pointers: they are purely a reference to a
+        memory address.
+
+        Fat pointers are pointers referencing "Dynamically Sized Types" (also
+        called DST). DST don't have a statically known size, therefore they can
+        only exist behind some kind of pointers that contain additional
+        information. Slices and trait objects are DSTs. In the case of slices,
+        the additional information the fat pointer holds is their size.
+
+        To fix this error, don't try to cast directly between thin and fat
+        pointers.
+
+        For more information about casts, take a look at The Book:
+        https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions",
+
 hir_typeck_cast_unknown_pointer = cannot cast {$to ->
     [true] to
     *[false] from
@@ -123,6 +139,16 @@ hir_typeck_option_result_asref = use `{$def_path}::as_ref` to convert `{$expecte
 hir_typeck_option_result_cloned = use `{$def_path}::cloned` to clone the value inside the `{$def_path}`
 hir_typeck_option_result_copied = use `{$def_path}::copied` to copy the value inside the `{$def_path}`
 
+hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function
+    .suggestion = cast the value to `{$cast_ty}`
+    .help = cast the value to `{$cast_ty}`
+    .teach_help = certain types, like `{$ty}`, must be casted before passing them to a variadic function, because of arcane ABI rules dictated by the C standard
+
+hir_typeck_ptr_cast_add_auto_to_object = adding {$traits_len ->
+    [1] an auto trait {$traits}
+    *[other] auto traits {$traits}
+} to a trait object in a pointer cast may cause UB later on
+
 hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression
 hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression
 hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 3b199b7e3c2..0d2a55a9507 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -24,8 +24,8 @@ use rustc_span::def_id::LocalDefId;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
 use rustc_target::spec::abi;
+use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::DefIdOrName;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 use std::{iter, slice};
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 92f2d3254bb..341d533492d 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -32,9 +32,9 @@ use super::FnCtxt;
 
 use crate::errors;
 use crate::type_error_struct;
-use hir::ExprKind;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{codes::*, Applicability, Diag, ErrorGuaranteed};
-use rustc_hir as hir;
+use rustc_hir::{self as hir, ExprKind};
 use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::bug;
 use rustc_middle::mir::Mutability;
@@ -44,7 +44,7 @@ use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::{self, Ty, TypeAndMut, TypeVisitableExt, VariantDef};
 use rustc_session::lint;
-use rustc_span::def_id::{DefId, LOCAL_CRATE};
+use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_span::DUMMY_SP;
@@ -73,7 +73,7 @@ enum PointerKind<'tcx> {
     /// No metadata attached, ie pointer to sized type or foreign type
     Thin,
     /// A trait object
-    VTable(Option<DefId>),
+    VTable(&'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>),
     /// Slice
     Length,
     /// The unsize info of this projection or opaque type
@@ -101,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         Ok(match *t.kind() {
             ty::Slice(_) | ty::Str => Some(PointerKind::Length),
-            ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())),
+            ty::Dynamic(tty, _, ty::Dyn) => Some(PointerKind::VTable(tty)),
             ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
                 None => Some(PointerKind::Thin),
                 Some(f) => {
@@ -319,7 +319,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 } else {
                     errors::CannotCastToBoolHelp::Unsupported(self.span)
                 };
-                fcx.tcx.dcx().emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help });
+                fcx.dcx().emit_err(errors::CannotCastToBool { span: self.span, expr_ty, help });
             }
             CastError::CastToChar => {
                 let mut err = type_error_struct!(
@@ -500,16 +500,12 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 err.emit();
             }
             CastError::SizedUnsizedCast => {
-                use rustc_hir_analysis::structured_errors::{SizedUnsizedCast, StructuredDiag};
-
-                SizedUnsizedCast {
-                    sess: fcx.tcx.sess,
+                fcx.dcx().emit_err(errors::CastThinPointerToFatPointer {
                     span: self.span,
                     expr_ty: self.expr_ty,
                     cast_ty: fcx.ty_to_string(self.cast_ty),
-                }
-                .diagnostic()
-                .emit();
+                    teach: fcx.tcx.sess.teach(E0607).then_some(()),
+                });
             }
             CastError::IntToFatCast(known_metadata) => {
                 let expr_if_nightly = fcx.tcx.sess.is_nightly_build().then_some(self.expr_span);
@@ -759,7 +755,7 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                 Err(CastError::IllegalCast)
             }
 
-            // ptr -> *
+            // ptr -> ptr
             (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast
 
             // ptr-addr-cast
@@ -803,40 +799,126 @@ impl<'a, 'tcx> CastCheck<'tcx> {
     fn check_ptr_ptr_cast(
         &self,
         fcx: &FnCtxt<'a, 'tcx>,
-        m_expr: ty::TypeAndMut<'tcx>,
-        m_cast: ty::TypeAndMut<'tcx>,
+        m_src: ty::TypeAndMut<'tcx>,
+        m_dst: ty::TypeAndMut<'tcx>,
     ) -> Result<CastKind, CastError> {
-        debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast);
+        debug!("check_ptr_ptr_cast m_src={m_src:?} m_dst={m_dst:?}");
         // ptr-ptr cast. vtables must match.
 
-        let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?;
-        let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?;
+        let src_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_src.ty, self.span)?);
+        let dst_kind = fcx.tcx.erase_regions(fcx.pointer_kind(m_dst.ty, self.span)?);
 
-        let Some(cast_kind) = cast_kind else {
+        match (src_kind, dst_kind) {
             // We can't cast if target pointer kind is unknown
-            return Err(CastError::UnknownCastPtrKind);
-        };
+            (_, None) => Err(CastError::UnknownCastPtrKind),
+            // Cast to thin pointer is OK
+            (_, Some(PointerKind::Thin)) => Ok(CastKind::PtrPtrCast),
 
-        // Cast to thin pointer is OK
-        if cast_kind == PointerKind::Thin {
-            return Ok(CastKind::PtrPtrCast);
-        }
-
-        let Some(expr_kind) = expr_kind else {
             // We can't cast to fat pointer if source pointer kind is unknown
-            return Err(CastError::UnknownExprPtrKind);
-        };
+            (None, _) => Err(CastError::UnknownExprPtrKind),
+
+            // thin -> fat? report invalid cast (don't complain about vtable kinds)
+            (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
+
+            // trait object -> trait object? need to do additional checks
+            (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
+                match (src_tty.principal(), dst_tty.principal()) {
+                    // A<dyn Src<...> + SrcAuto> -> B<dyn Dst<...> + DstAuto>. need to make sure
+                    // - `Src` and `Dst` traits are the same
+                    // - traits have the same generic arguments
+                    // - `SrcAuto` is a superset of `DstAuto`
+                    (Some(src_principal), Some(dst_principal)) => {
+                        let tcx = fcx.tcx;
+
+                        // Check that the traits are actually the same.
+                        // The `dyn Src = dyn Dst` check below would suffice,
+                        // but this may produce a better diagnostic.
+                        //
+                        // Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
+                        // and is unaffected by this check.
+                        if src_principal.def_id() != dst_principal.def_id() {
+                            return Err(CastError::DifferingKinds);
+                        }
 
-        // thin -> fat? report invalid cast (don't complain about vtable kinds)
-        if expr_kind == PointerKind::Thin {
-            return Err(CastError::SizedUnsizedCast);
-        }
+                        // We need to reconstruct trait object types.
+                        // `m_src` and `m_dst` won't work for us here because they will potentially
+                        // contain wrappers, which we do not care about.
+                        //
+                        // e.g. we want to allow `dyn T -> (dyn T,)`, etc.
+                        //
+                        // We also need to skip auto traits to emit an FCW and not an error.
+                        let src_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                            tcx.mk_poly_existential_predicates(
+                                &src_tty.without_auto_traits().collect::<Vec<_>>(),
+                            ),
+                            tcx.lifetimes.re_erased,
+                            ty::Dyn,
+                        ));
+                        let dst_obj = tcx.mk_ty_from_kind(ty::Dynamic(
+                            tcx.mk_poly_existential_predicates(
+                                &dst_tty.without_auto_traits().collect::<Vec<_>>(),
+                            ),
+                            tcx.lifetimes.re_erased,
+                            ty::Dyn,
+                        ));
 
-        // vtable kinds must match
-        if fcx.tcx.erase_regions(cast_kind) == fcx.tcx.erase_regions(expr_kind) {
-            Ok(CastKind::PtrPtrCast)
-        } else {
-            Err(CastError::DifferingKinds)
+                        // `dyn Src = dyn Dst`, this checks for matching traits/generics
+                        fcx.demand_eqtype(self.span, src_obj, dst_obj);
+
+                        // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
+                        // Emit an FCW otherwise.
+                        let src_auto: FxHashSet<_> = src_tty
+                            .auto_traits()
+                            .chain(
+                                tcx.supertrait_def_ids(src_principal.def_id())
+                                    .filter(|def_id| tcx.trait_is_auto(*def_id)),
+                            )
+                            .collect();
+
+                        let added = dst_tty
+                            .auto_traits()
+                            .filter(|trait_did| !src_auto.contains(trait_did))
+                            .collect::<Vec<_>>();
+
+                        if !added.is_empty() {
+                            tcx.emit_node_span_lint(
+                                lint::builtin::PTR_CAST_ADD_AUTO_TO_OBJECT,
+                                self.expr.hir_id,
+                                self.span,
+                                errors::PtrCastAddAutoToObject {
+                                    traits_len: added.len(),
+                                    traits: {
+                                        let mut traits: Vec<_> = added
+                                            .into_iter()
+                                            .map(|trait_did| tcx.def_path_str(trait_did))
+                                            .collect();
+
+                                        traits.sort();
+                                        traits.into()
+                                    },
+                                },
+                            )
+                        }
+
+                        Ok(CastKind::PtrPtrCast)
+                    }
+
+                    // dyn Auto -> dyn Auto'? ok.
+                    (None, None) => Ok(CastKind::PtrPtrCast),
+
+                    // dyn Trait -> dyn Auto? should be ok, but we used to not allow it.
+                    // FIXME: allow this
+                    (Some(_), None) => Err(CastError::DifferingKinds),
+
+                    // dyn Auto -> dyn Trait? not ok.
+                    (None, Some(_)) => Err(CastError::DifferingKinds),
+                }
+            }
+
+            // fat -> fat? metadata kinds must match
+            (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(CastKind::PtrPtrCast),
+
+            (_, _) => Err(CastError::DifferingKinds),
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs
index ac7ed3e26f9..08de871f6fa 100644
--- a/compiler/rustc_hir_typeck/src/closure.rs
+++ b/compiler/rustc_hir_typeck/src/closure.rs
@@ -17,9 +17,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::ArgKind;
+use rustc_trait_selection::error_reporting::traits::InferCtxtExt as _;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::ArgKind;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 use rustc_type_ir::ClosureKind;
 use std::iter;
 use std::ops::ControlFlow;
@@ -424,9 +424,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if let Some(trait_def_id) = trait_def_id {
                 let found_kind = match closure_kind {
                     hir::ClosureKind::Closure => self.tcx.fn_trait_kind_from_def_id(trait_def_id),
-                    hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
-                        self.tcx.async_fn_trait_kind_from_def_id(trait_def_id)
-                    }
+                    hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => self
+                        .tcx
+                        .async_fn_trait_kind_from_def_id(trait_def_id)
+                        .or_else(|| self.tcx.fn_trait_kind_from_def_id(trait_def_id)),
                     _ => None,
                 };
 
@@ -470,14 +471,37 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // for closures and async closures, respectively.
         match closure_kind {
             hir::ClosureKind::Closure
-                if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
+                if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
+            {
+                self.extract_sig_from_projection(cause_span, projection)
+            }
+            hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
+                if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() =>
+            {
+                self.extract_sig_from_projection(cause_span, projection)
+            }
+            // It's possible we've passed the closure to a (somewhat out-of-fashion)
+            // `F: FnOnce() -> Fut, Fut: Future<Output = T>` style bound. Let's still
+            // guide inference here, since it's beneficial for the user.
             hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async)
-                if self.tcx.async_fn_trait_kind_from_def_id(trait_def_id).is_some() => {}
-            _ => return None,
+                if self.tcx.fn_trait_kind_from_def_id(trait_def_id).is_some() =>
+            {
+                self.extract_sig_from_projection_and_future_bound(cause_span, projection)
+            }
+            _ => None,
         }
+    }
+
+    /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args
+    /// and return type to infer a [`ty::PolyFnSig`] for the closure.
+    fn extract_sig_from_projection(
+        &self,
+        cause_span: Option<Span>,
+        projection: ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<ExpectedSig<'tcx>> {
+        let projection = self.resolve_vars_if_possible(projection);
 
         let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
-        let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty);
         debug!(?arg_param_ty);
 
         let ty::Tuple(input_tys) = *arg_param_ty.kind() else {
@@ -486,7 +510,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // Since this is a return parameter type it is safe to unwrap.
         let ret_param_ty = projection.skip_binder().term.expect_type();
-        let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty);
         debug!(?ret_param_ty);
 
         let sig = projection.rebind(self.tcx.mk_fn_sig(
@@ -500,6 +523,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         Some(ExpectedSig { cause_span, sig })
     }
 
+    /// When an async closure is passed to a function that has a "two-part" `Fn`
+    /// and `Future` trait bound, like:
+    ///
+    /// ```rust
+    /// use std::future::Future;
+    ///
+    /// fn not_exactly_an_async_closure<F, Fut>(_f: F)
+    /// where
+    ///     F: FnOnce(String, u32) -> Fut,
+    ///     Fut: Future<Output = i32>,
+    /// {}
+    /// ```
+    ///
+    /// The we want to be able to extract the signature to guide inference in the async
+    /// closure. We will have two projection predicates registered in this case. First,
+    /// we identify the `FnOnce<Args, Output = ?Fut>` bound, and if the output type is
+    /// an inference variable `?Fut`, we check if that is bounded by a `Future<Output = Ty>`
+    /// projection.
+    fn extract_sig_from_projection_and_future_bound(
+        &self,
+        cause_span: Option<Span>,
+        projection: ty::PolyProjectionPredicate<'tcx>,
+    ) -> Option<ExpectedSig<'tcx>> {
+        let projection = self.resolve_vars_if_possible(projection);
+
+        let arg_param_ty = projection.skip_binder().projection_term.args.type_at(1);
+        debug!(?arg_param_ty);
+
+        let ty::Tuple(input_tys) = *arg_param_ty.kind() else {
+            return None;
+        };
+
+        // If the return type is a type variable, look for bounds on it.
+        // We could theoretically support other kinds of return types here,
+        // but none of them would be useful, since async closures return
+        // concrete anonymous future types, and their futures are not coerced
+        // into any other type within the body of the async closure.
+        let ty::Infer(ty::TyVar(return_vid)) = *projection.skip_binder().term.expect_type().kind()
+        else {
+            return None;
+        };
+
+        // FIXME: We may want to elaborate here, though I assume this will be exceedingly rare.
+        for bound in self.obligations_for_self_ty(return_vid) {
+            if let Some(ret_projection) = bound.predicate.as_projection_clause()
+                && let Some(ret_projection) = ret_projection.no_bound_vars()
+                && self.tcx.is_lang_item(ret_projection.def_id(), LangItem::FutureOutput)
+            {
+                let sig = projection.rebind(self.tcx.mk_fn_sig(
+                    input_tys,
+                    ret_projection.term.expect_type(),
+                    false,
+                    hir::Safety::Safe,
+                    Abi::Rust,
+                ));
+
+                return Some(ExpectedSig { cause_span, sig });
+            }
+        }
+
+        None
+    }
+
     fn sig_of_closure(
         &self,
         expr_def_id: LocalDefId,
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 0551b9bc1f0..5e2a68e1f02 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -58,9 +58,9 @@ use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
 use rustc_span::{BytePos, DesugaringKind, Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtSelectionErrExt as _;
 use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::{
     self, NormalizeExt, ObligationCause, ObligationCauseCode, ObligationCtxt,
@@ -1752,10 +1752,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
             fcx.probe(|_| {
                 let ocx = ObligationCtxt::new(fcx);
                 ocx.register_obligations(
-                    fcx.tcx
-                        .item_super_predicates(rpit_def_id)
-                        .instantiate_identity_iter()
-                        .filter_map(|clause| {
+                    fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map(
+                        |clause| {
                             let predicate = clause
                                 .kind()
                                 .map_bound(|clause| match clause {
@@ -1776,7 +1774,8 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                                 fcx.param_env,
                                 predicate,
                             ))
-                        }),
+                        },
+                    ),
                 );
                 ocx.select_where_possible().is_empty()
             })
@@ -1797,16 +1796,16 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends });
     }
 
-    fn report_return_mismatched_types<'a>(
+    fn report_return_mismatched_types<'infcx>(
         &self,
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         ty_err: TypeError<'tcx>,
-        fcx: &FnCtxt<'a, 'tcx>,
+        fcx: &'infcx FnCtxt<'_, 'tcx>,
         block_or_return_id: hir::HirId,
         expression: Option<&'tcx hir::Expr<'tcx>>,
-    ) -> Diag<'a> {
+    ) -> Diag<'infcx> {
         let mut err = fcx.err_ctxt().report_mismatched_types(cause, expected, found, ty_err);
 
         let due_to_block = matches!(fcx.tcx.hir_node(block_or_return_id), hir::Node::Block(..));
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index f9720c9c307..4f1c2fdd922 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -13,6 +13,7 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self, AssocItem, Ty, TypeFoldable, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::ObligationCause;
 
 use super::method::probe;
@@ -172,21 +173,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn demand_suptype_diag(
-        &self,
+        &'a self,
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'a>> {
         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
     }
 
     #[instrument(skip(self), level = "debug")]
     pub fn demand_suptype_with_origin(
-        &self,
+        &'a self,
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'a>> {
         self.at(cause, self.param_env)
             .sup(DefineOpaqueTypes::Yes, expected, actual)
             .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
@@ -200,20 +201,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     pub fn demand_eqtype_diag(
-        &self,
+        &'a self,
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'a>> {
         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
     }
 
     pub fn demand_eqtype_with_origin(
-        &self,
+        &'a self,
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'a>> {
         self.at(cause, self.param_env)
             .eq(DefineOpaqueTypes::Yes, expected, actual)
             .map(|infer_ok| self.register_infer_ok_obligations(infer_ok))
@@ -247,13 +248,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// will be permitted if the diverges flag is currently "always".
     #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))]
     pub fn demand_coerce_diag(
-        &self,
+        &'a self,
         mut expr: &'tcx hir::Expr<'tcx>,
         checked_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
         mut expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         allow_two_phase: AllowTwoPhase,
-    ) -> Result<Ty<'tcx>, Diag<'tcx>> {
+    ) -> Result<Ty<'tcx>, Diag<'a>> {
         let expected = self.resolve_vars_with_obligations(expected);
 
         let e = match self.coerce(expr, checked_ty, expected, allow_two_phase, None) {
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 98add86252c..9a38d6d4a71 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -4,8 +4,8 @@ use std::borrow::Cow;
 
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagArgValue, EmissionGuarantee, IntoDiagArg, MultiSpan,
-    SubdiagMessageOp, Subdiagnostic,
+    codes::*, Applicability, Diag, DiagArgValue, DiagSymbolList, EmissionGuarantee, IntoDiagArg,
+    MultiSpan, SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
@@ -253,6 +253,13 @@ pub struct LossyProvenanceInt2Ptr<'tcx> {
     pub sugg: LossyProvenanceInt2PtrSuggestion,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(hir_typeck_ptr_cast_add_auto_to_object)]
+pub struct PtrCastAddAutoToObject {
+    pub traits_len: usize,
+    pub traits: DiagSymbolList<String>,
+}
+
 #[derive(Subdiagnostic)]
 #[multipart_suggestion(hir_typeck_suggestion, applicability = "has-placeholders")]
 pub struct LossyProvenanceInt2PtrSuggestion {
@@ -501,6 +508,7 @@ pub enum SuggestBoxing {
 #[suggestion(
     hir_typeck_suggest_ptr_null_mut,
     applicability = "maybe-incorrect",
+    style = "verbose",
     code = "core::ptr::null_mut()"
 )]
 pub struct SuggestPtrNullMut {
@@ -689,3 +697,30 @@ pub struct ReplaceWithName {
     pub span: Span,
     pub name: String,
 }
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_cast_thin_pointer_to_fat_pointer, code = E0607)]
+pub(crate) struct CastThinPointerToFatPointer<'tcx> {
+    #[primary_span]
+    pub span: Span,
+    pub expr_ty: Ty<'tcx>,
+    pub cast_ty: String,
+    #[note(hir_typeck_teach_help)]
+    pub(crate) teach: Option<()>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_typeck_pass_to_variadic_function, code = E0617)]
+pub(crate) struct PassToVariadicFunction<'tcx, 'a> {
+    #[primary_span]
+    pub span: Span,
+    pub ty: Ty<'tcx>,
+    pub cast_ty: &'a str,
+    #[suggestion(code = "{replace}", applicability = "machine-applicable")]
+    pub sugg_span: Option<Span>,
+    pub replace: String,
+    #[help]
+    pub help: Option<()>,
+    #[note(hir_typeck_teach_help)]
+    pub(crate) teach: Option<()>,
+}
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index f4e1e461953..432489330a6 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -53,9 +53,9 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
 use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::ObligationCtxt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode};
 
@@ -638,7 +638,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Set expectation to error in that case and set tainted
                 // by error (#114529)
                 let coerce_to = opt_coerce_to.unwrap_or_else(|| {
-                    let guar = tcx.dcx().span_delayed_bug(
+                    let guar = self.dcx().span_delayed_bug(
                         expr.span,
                         "illegal break with value found but no error reported",
                     );
@@ -708,7 +708,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // else an error would have been flagged by the
                 // `loops` pass for using break with an expression
                 // where you are not supposed to.
-                assert!(expr_opt.is_none() || self.dcx().has_errors().is_some());
+                assert!(expr_opt.is_none() || self.tainted_by_errors().is_some());
             }
 
             // If we encountered a `break`, then (no surprise) it may be possible to break from the
@@ -1716,7 +1716,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 error_happened = true;
                 let guar = if let Some(prev_span) = seen_fields.get(&ident) {
-                    tcx.dcx().emit_err(FieldMultiplySpecifiedInInitializer {
+                    self.dcx().emit_err(FieldMultiplySpecifiedInInitializer {
                         span: field.ident.span,
                         prev_span: *prev_span,
                         ident,
@@ -1757,7 +1757,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if adt_kind == AdtKind::Union {
             if hir_fields.len() != 1 {
                 struct_span_code_err!(
-                    tcx.dcx(),
+                    self.dcx(),
                     span,
                     E0784,
                     "union expressions should have exactly one field",
@@ -2551,10 +2551,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         match *base_ty.peel_refs().kind() {
             ty::Array(_, len) => {
-                self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
+                self.maybe_suggest_array_indexing(&mut err, base, ident, len);
             }
             ty::RawPtr(..) => {
-                self.suggest_first_deref_field(&mut err, expr, base, ident);
+                self.suggest_first_deref_field(&mut err, base, ident);
             }
             ty::Param(param_ty) => {
                 err.span_label(ident.span, "unknown field");
@@ -2721,7 +2721,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn maybe_suggest_array_indexing(
         &self,
         err: &mut Diag<'_>,
-        expr: &hir::Expr<'_>,
         base: &hir::Expr<'_>,
         field: Ident,
         len: ty::Const<'tcx>,
@@ -2729,32 +2728,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.span_label(field.span, "unknown field");
         if let (Some(len), Ok(user_index)) =
             (len.try_eval_target_usize(self.tcx, self.param_env), field.as_str().parse::<u64>())
-            && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span)
         {
             let help = "instead of using tuple indexing, use array indexing";
-            let suggestion = format!("{base}[{field}]");
             let applicability = if len < user_index {
                 Applicability::MachineApplicable
             } else {
                 Applicability::MaybeIncorrect
             };
-            err.span_suggestion(expr.span, help, suggestion, applicability);
+            err.multipart_suggestion(
+                help,
+                vec![
+                    (base.span.between(field.span), "[".to_string()),
+                    (field.span.shrink_to_hi(), "]".to_string()),
+                ],
+                applicability,
+            );
         }
     }
 
-    fn suggest_first_deref_field(
-        &self,
-        err: &mut Diag<'_>,
-        expr: &hir::Expr<'_>,
-        base: &hir::Expr<'_>,
-        field: Ident,
-    ) {
+    fn suggest_first_deref_field(&self, err: &mut Diag<'_>, base: &hir::Expr<'_>, field: Ident) {
         err.span_label(field.span, "unknown field");
-        if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) {
-            let msg = format!("`{base}` is a raw pointer; try dereferencing it");
-            let suggestion = format!("(*{base}).{field}");
-            err.span_suggestion(expr.span, msg, suggestion, Applicability::MaybeIncorrect);
-        }
+        let val = if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span)
+            && base.len() < 20
+        {
+            format!("`{base}`")
+        } else {
+            "the value".to_string()
+        };
+        err.multipart_suggestion(
+            format!("{val} is a raw pointer; try dereferencing it"),
+            vec![
+                (base.span.shrink_to_lo(), "(*".to_string()),
+                (base.span.shrink_to_hi(), ")".to_string()),
+            ],
+            Applicability::MaybeIncorrect,
+        );
     }
 
     fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'_> {
diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
index 0ba4bd090f5..193dbbbcdf4 100644
--- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs
@@ -170,7 +170,7 @@ impl<'tcx> TypeInformationCtxt<'tcx> for &FnCtxt<'_, 'tcx> {
     }
 
     fn report_error(&self, span: Span, msg: impl ToString) -> Self::Error {
-        self.tcx.dcx().span_delayed_bug(span, msg.to_string())
+        self.dcx().span_delayed_bug(span, msg.to_string())
     }
 
     fn error_reported_in_ty(&self, ty: Ty<'tcx>) -> Result<(), Self::Error> {
@@ -734,7 +734,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
                 // struct; however, when EUV is run during typeck, it
                 // may not. This will generate an error earlier in typeck,
                 // so we can just ignore it.
-                if self.cx.tcx().dcx().has_errors().is_none() {
+                if self.cx.tainted_by_errors().is_ok() {
                     span_bug!(with_expr.span, "with expression doesn't evaluate to a struct");
                 }
             }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 061afd03062..ea9567f4e3d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -37,7 +37,7 @@ use rustc_span::hygiene::DesugaringKind;
 use rustc_span::symbol::{kw, sym};
 use rustc_span::Span;
 use rustc_target::abi::FieldIdx;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{
     self, NormalizeExt, ObligationCauseCode, ObligationCtxt, StructurallyNormalizeExt,
 };
@@ -1139,7 +1139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // parameter's value explicitly, so we have to do some error-
             // checking here.
             let arg_count =
-                check_generic_arg_count_for_call(tcx, def_id, generics, seg, IsMethodCall::No);
+                check_generic_arg_count_for_call(self, def_id, generics, seg, IsMethodCall::No);
 
             if let ExplicitLateBound::Yes = arg_count.explicit_late_bound {
                 explicit_late_bound = ExplicitLateBound::Yes;
@@ -1182,7 +1182,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     name: self.tcx.item_name(def.did()).to_ident_string(),
                 });
                 if ty.raw.has_param() {
-                    let guar = self.tcx.dcx().emit_err(errors::SelfCtorFromOuterItem {
+                    let guar = self.dcx().emit_err(errors::SelfCtorFromOuterItem {
                         span: path_span,
                         impl_span: tcx.def_span(impl_def_id),
                         sugg,
@@ -1207,7 +1207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // Check the visibility of the ctor.
                     let vis = tcx.visibility(ctor_def_id);
                     if !vis.is_accessible_from(tcx.parent_module(hir_id).to_def_id(), tcx) {
-                        tcx.dcx()
+                        self.dcx()
                             .emit_err(CtorIsPrivate { span, def: tcx.def_path_str(adt_def.did()) });
                     }
                     let new_res = Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
@@ -1216,7 +1216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     (new_res, Some(user_args.args))
                 }
                 _ => {
-                    let mut err = tcx.dcx().struct_span_err(
+                    let mut err = self.dcx().struct_span_err(
                         span,
                         "the `Self` constructor can only be used with tuple or unit structs",
                     );
@@ -1304,7 +1304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         self.fcx.ty_infer(Some(param), inf.span).into()
                     }
                     (
-                        &GenericParamDefKind::Const { has_default, is_host_effect },
+                        &GenericParamDefKind::Const { has_default, is_host_effect, .. },
                         GenericArg::Infer(inf),
                     ) => {
                         if has_default && is_host_effect {
@@ -1346,7 +1346,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             self.fcx.var_for_def(self.span, param)
                         }
                     }
-                    GenericParamDefKind::Const { has_default, is_host_effect } => {
+                    GenericParamDefKind::Const { has_default, is_host_effect, .. } => {
                         if has_default {
                             // N.B. this is a bit of a hack. `infer_args` is passed depending on
                             // whether the user has provided generic args. E.g. for `Vec::new`
@@ -1375,7 +1375,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let args_raw = self_ctor_args.unwrap_or_else(|| {
             lower_generic_args(
-                tcx,
+                self,
                 def_id,
                 &[],
                 has_self,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 1138642c56d..ab0f356ce91 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -28,7 +28,6 @@ use rustc_hir::{ExprKind, HirId, Node, QPath};
 use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
 use rustc_hir_analysis::check::potentially_plural_count;
 use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
-use rustc_hir_analysis::structured_errors::StructuredDiag;
 use rustc_index::IndexVec;
 use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
 use rustc_infer::infer::TypeTrace;
@@ -40,6 +39,7 @@ use rustc_middle::{bug, span_bug};
 use rustc_session::Session;
 use rustc_span::symbol::{kw, Ident};
 use rustc_span::{sym, BytePos, Span, DUMMY_SP};
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext};
 
 use std::iter;
@@ -238,7 +238,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
                     let guar = struct_span_code_err!(
-                        tcx.dcx(),
+                        self.dcx(),
                         call_span,
                         E0059,
                         "cannot use call notation; the first type parameter \
@@ -405,9 +405,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     ty: Ty<'tcx>,
                     cast_ty: &str,
                 ) {
-                    use rustc_hir_analysis::structured_errors::MissingCastForVariadicArg;
+                    let (sugg_span, replace, help) =
+                        if let Ok(snippet) = sess.source_map().span_to_snippet(span) {
+                            (Some(span), format!("{snippet} as {cast_ty}"), None)
+                        } else {
+                            (None, "".to_string(), Some(()))
+                        };
 
-                    MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
+                    sess.dcx().emit_err(errors::PassToVariadicFunction {
+                        span,
+                        ty,
+                        cast_ty,
+                        help,
+                        replace,
+                        sugg_span,
+                        teach: sess.teach(E0617).then_some(()),
+                    });
                 }
 
                 // There are a few types which get autopromoted when passed via varargs
@@ -453,7 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .map(|vars| self.resolve_vars_if_possible(vars)),
             );
 
-            self.set_tainted_by_errors(self.report_arg_errors(
+            self.report_arg_errors(
                 compatibility_diagonal,
                 formal_and_expected_inputs,
                 provided_args,
@@ -462,7 +475,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 fn_def_id,
                 call_span,
                 call_expr,
-            ));
+            );
         }
     }
 
@@ -788,7 +801,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             format!("arguments to this {call_name} are incorrect"),
                         );
                     } else {
-                        err = tcx.dcx().struct_span_err(
+                        err = self.dcx().struct_span_err(
                             full_call_span,
                             format!(
                                 "{call_name} takes {}{} but {} {} supplied",
@@ -848,7 +861,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 span_bug!(error_span, "expected errors from argument matrix");
             } else {
                 let mut err =
-                    tcx.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span });
+                    self.dcx().create_err(errors::ArgMismatchIndeterminate { span: error_span });
                 suggest_confusable(&mut err);
                 return err.emit();
             }
@@ -951,16 +964,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return err.emit();
         }
 
+        // Special case, we found an extra argument is provided, which is very common in practice.
+        // but there is a obviously better removing suggestion compared to the current one,
+        // try to find the argument with Error type, if we removed it all the types will become good,
+        // then we will replace the current suggestion.
+        if let [Error::Extra(provided_idx)] = &errors[..] {
+            let remove_idx_is_perfect = |idx: usize| -> bool {
+                let removed_arg_tys = provided_arg_tys
+                    .iter()
+                    .enumerate()
+                    .filter_map(|(j, arg)| if idx == j { None } else { Some(arg) })
+                    .collect::<IndexVec<ProvidedIdx, _>>();
+                std::iter::zip(formal_and_expected_inputs.iter(), removed_arg_tys.iter()).all(
+                    |((expected_ty, _), (provided_ty, _))| {
+                        !provided_ty.references_error()
+                            && self.can_coerce(*provided_ty, *expected_ty)
+                    },
+                )
+            };
+
+            if !remove_idx_is_perfect(provided_idx.as_usize()) {
+                if let Some(i) = (0..provided_args.len()).find(|&i| remove_idx_is_perfect(i)) {
+                    errors = vec![Error::Extra(ProvidedIdx::from_usize(i))];
+                }
+            }
+        }
+
         let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
             struct_span_code_err!(
-                tcx.dcx(),
+                self.dcx(),
                 full_call_span,
                 E0308,
                 "arguments to this {} are incorrect",
                 call_name,
             )
         } else {
-            tcx.dcx()
+            self.dcx()
                 .struct_span_err(
                     full_call_span,
                     format!(
@@ -1358,7 +1397,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Some(format!("provide the argument{}", if plural { "s" } else { "" }))
             }
             SuggestionText::Remove(plural) => {
-                err.multipart_suggestion(
+                err.multipart_suggestion_verbose(
                     format!("remove the extra argument{}", if plural { "s" } else { "" }),
                     suggestions,
                     Applicability::HasPlaceholders,
@@ -1424,7 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected_ty: Ty<'tcx>,
         provided_ty: Ty<'tcx>,
         arg: &hir::Expr<'tcx>,
-        err: &mut Diag<'tcx>,
+        err: &mut Diag<'_>,
     ) {
         if let ty::RawPtr(_, hir::Mutability::Mut) = expected_ty.kind()
             && let ty::RawPtr(_, hir::Mutability::Not) = provided_ty.kind()
@@ -1652,7 +1691,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement");
 
-        // Hide the outer diverging and `has_errors` flags.
+        // Hide the outer diverging flags.
         let old_diverges = self.diverges.replace(Diverges::Maybe);
 
         match stmt.kind {
@@ -2042,7 +2081,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                     if block_num > 1 && found_semi {
                         err.span_suggestion_verbose(
-                            span.shrink_to_lo(),
+                            // use the span of the *whole* expr
+                            self.tcx.hir().span(binding_hir_id).shrink_to_lo(),
                             "you might have meant to return this to infer its type parameters",
                             "return ",
                             Applicability::MaybeIncorrect,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
index fab7eb7495c..90dd5f73586 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs
@@ -104,8 +104,6 @@ struct NestedObligationsForSelfTy<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> {
-    type Result = ();
-
     fn span(&self) -> Span {
         self.root_cause.span
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index c0d60477967..b5796fbd48a 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 inspect_obligations;
 mod suggestions;
 
-use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
+use rustc_errors::DiagCtxtHandle;
 
 use crate::coercion::DynamicCoerceMany;
 use crate::fallback::DivergingFallbackBehavior;
@@ -144,8 +144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {
-        self.infcx.dcx()
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'a> {
+        self.root_ctxt.infcx.dcx()
     }
 
     pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
@@ -217,6 +217,10 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         self.tcx
     }
 
+    fn dcx(&self) -> DiagCtxtHandle<'_> {
+        self.root_ctxt.dcx()
+    }
+
     fn item_def_id(&self) -> LocalDefId {
         self.body_id
     }
@@ -274,6 +278,7 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
                     }
                 }),
             ),
+            effects_min_tys: ty::List::empty(),
         }
     }
 
@@ -337,10 +342,6 @@ impl<'tcx> HirTyLowerer<'tcx> for FnCtxt<'_, 'tcx> {
         Some(&self.infcx)
     }
 
-    fn set_tainted_by_errors(&self, e: ErrorGuaranteed) {
-        self.infcx.set_tainted_by_errors(e)
-    }
-
     fn lower_fn_sig(
         &self,
         decl: &rustc_hir::FnDecl<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 8d380caf916..5f897c74482 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -32,10 +32,10 @@ use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::{Span, Symbol};
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::DefIdOrName;
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
-use rustc_trait_selection::traits::error_reporting::DefIdOrName;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -2582,7 +2582,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
             }
             (hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr), _, &ty::Ref(_, checked, _))
-                if self.can_sub(self.param_env, checked, expected) =>
+                if self.can_eq(self.param_env, checked, expected) =>
             {
                 let make_sugg = |start: Span, end: BytePos| {
                     // skip `(` for tuples such as `(c) = (&123)`.
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 5eafc60a04e..0389c06c312 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -52,7 +52,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Note: this path is currently not reached in any test, so any
             // example that triggers this would be worth minimizing and
             // converting into a test.
-            tcx.dcx().span_bug(span, "argument to transmute has inference variables");
+            self.dcx().span_bug(span, "argument to transmute has inference variables");
         }
         // Transmutes that are only changing lifetimes are always ok.
         if from == to {
@@ -76,7 +76,7 @@ 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_code_err!(tcx.dcx(), span, E0591, "can't transmute zero-sized type")
+                struct_span_code_err!(self.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")
@@ -116,7 +116,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
 
         let mut err = struct_span_code_err!(
-            tcx.dcx(),
+            self.dcx(),
             span,
             E0512,
             "cannot transmute between types of different sizes, \
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 120e3239d1f..e574fde14fb 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -354,7 +354,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         let generics = self.tcx.generics_of(pick.item.def_id);
 
         let arg_count_correct = check_generic_arg_count_for_call(
-            self.tcx,
+            self.fcx,
             pick.item.def_id,
             generics,
             seg,
@@ -425,7 +425,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         }
 
         let args = lower_generic_args(
-            self.tcx,
+            self.fcx,
             pick.item.def_id,
             parent_args,
             false,
@@ -510,9 +510,12 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                         .report_mismatched_types(&cause, method_self_ty, self_ty, terr)
                         .emit();
                 } else {
-                    error!("{self_ty} was a subtype of {method_self_ty} but now is not?");
-                    // This must already have errored elsewhere.
-                    self.dcx().has_errors().unwrap();
+                    // This has/will have errored in wfcheck, which we cannot depend on from here, as typeck on functions
+                    // may run before wfcheck if the function is used in const eval.
+                    self.dcx().span_delayed_bug(
+                        cause.span(),
+                        format!("{self_ty} was a subtype of {method_self_ty} but now is not?"),
+                    );
                 }
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index e1223307b53..dc1b888374c 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -356,6 +356,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
         let (obligation, args) =
             self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types);
+        // FIXME(effects) find a better way to do this
+        // Operators don't have generic methods, but making them `#[const_trait]` gives them
+        // `const host: bool`.
+        let args = if self.tcx.is_const_trait(trait_def_id) {
+            self.tcx.mk_args_from_iter(
+                args.iter()
+                    .chain([self.tcx.expected_host_effect_param_for_body(self.body_id).into()]),
+            )
+        } else {
+            args
+        };
         self.construct_obligation_for_trait(m_name, trait_def_id, obligation, args)
     }
 
@@ -393,6 +404,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         debug!("lookup_in_trait_adjusted: method_item={:?}", method_item);
         let mut obligations = vec![];
 
+        // FIXME(effects): revisit when binops get `#[const_trait]`
+
         // Instantiate late-bound regions and instantiate the trait
         // parameters into the method type to get the actual method type.
         //
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 9747a91ccbf..6a7af5510e0 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -33,6 +33,7 @@ use rustc_span::edit_distance::{
 };
 use rustc_span::symbol::sym;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
+use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy;
 use rustc_trait_selection::traits::query::method_autoderef::{
@@ -857,7 +858,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 let args = self.fresh_args_for_item(self.span, method.def_id);
                 let fty = self.tcx.fn_sig(method.def_id).instantiate(self.tcx, args);
                 let fty = self.instantiate_binder_with_fresh_vars(self.span, infer::FnCall, fty);
-                self.can_sub(self.param_env, fty.output(), expected)
+                self.can_eq(self.param_env, fty.output(), expected)
             }),
             _ => false,
         }
@@ -1279,6 +1280,7 @@ impl<'tcx> Pick<'tcx> {
                     trait_item_def_id: _,
                     fn_has_self_parameter: _,
                     opt_rpitit_info: _,
+                    is_effects_desugaring: _,
                 },
             kind: _,
             import_ids: _,
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index a385bc70e35..425289ce3c5 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -35,9 +35,9 @@ use rustc_span::def_id::DefIdSet;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{edit_distance, ErrorGuaranteed, ExpnKind, FileName, MacroKind, Span};
 use rustc_span::{Symbol, DUMMY_SP};
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::OnUnimplementedNote;
+use rustc_trait_selection::error_reporting::traits::on_unimplemented::TypeErrCtxtExt as _;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
-use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
     supertraits, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
@@ -499,7 +499,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     }
                 }
 
-                // If the shadowed binding has an an itializer expression,
+                // If the shadowed binding has an itializer expression,
                 // use the initializer expression'ty to try to find the method again.
                 // For example like:  `let mut x = Vec::new();`,
                 // `Vec::new()` is the itializer expression.
@@ -705,7 +705,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source {
             self.suggest_missing_writer(rcvr_ty, rcvr_expr)
         } else {
-            let mut err = tcx.dcx().create_err(NoAssociatedItem {
+            let mut err = self.dcx().create_err(NoAssociatedItem {
                 span,
                 item_kind,
                 item_name,
@@ -968,7 +968,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             // Make sure that, if any traits other than the found ones were involved,
-            // we don't don't report an unimplemented trait.
+            // we don't report an unimplemented trait.
             // We don't want to say that `iter::Cloned` is not an iterator, just
             // because of some non-Clone item being iterated over.
             for (predicate, _parent_pred, _cause) in unsatisfied_predicates {
@@ -1194,7 +1194,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         span: item_span,
                         ..
                     })) => {
-                        tcx.dcx().span_delayed_bug(
+                        self.dcx().span_delayed_bug(
                             *item_span,
                             "auto trait is invoked with no method error, but no error reported?",
                         );
@@ -2129,7 +2129,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             let target_ty = self
                 .autoderef(sugg_span, rcvr_ty)
                 .find(|(rcvr_ty, _)| {
-                    DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey }
+                    DeepRejectCtxt::new(self.tcx, TreatParams::ForLookup)
                         .types_may_unify(*rcvr_ty, impl_ty)
                 })
                 .map_or(impl_ty, |(ty, _)| ty)
@@ -2361,7 +2361,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     );
                     if pick.is_ok() {
                         let range_span = parent_expr.span.with_hi(expr.span.hi());
-                        return Err(tcx.dcx().emit_err(errors::MissingParenthesesInRange {
+                        return Err(self.dcx().emit_err(errors::MissingParenthesesInRange {
                             span,
                             ty_str: ty_str.to_string(),
                             method_name: item_name.as_str().to_string(),
@@ -2420,7 +2420,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && let SelfSource::MethodCall(expr) = source
         {
             let mut err = struct_span_code_err!(
-                tcx.dcx(),
+                self.dcx(),
                 span,
                 E0689,
                 "can't call {} `{}` on ambiguous numeric type `{}`",
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 5a11cb7096f..d59b8276d3a 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -18,8 +18,8 @@ use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Ident};
 use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{FulfillmentError, ObligationCtxt};
 use rustc_type_ir::TyKind::*;
 
@@ -838,8 +838,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     },
                                 ) = ex.kind
                                 {
-                                    err.span_suggestion(
-                                        ex.span,
+                                    let span = if let hir::Node::Expr(parent) =
+                                        self.tcx.parent_hir_node(ex.hir_id)
+                                        && let hir::ExprKind::Cast(..) = parent.kind
+                                    {
+                                        // `-1 as usize` -> `usize::MAX`
+                                        parent.span
+                                    } else {
+                                        ex.span
+                                    };
+                                    err.span_suggestion_verbose(
+                                        span,
                                         format!(
                                             "you may have meant the maximum value of `{actual}`",
                                         ),
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index aaf3d3ec34d..be526e1c26c 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -19,6 +19,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 use rustc_target::abi::FieldIdx;
+use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
 use ty::VariantDef;
 
@@ -89,7 +90,7 @@ struct PatInfo<'tcx, 'a> {
     current_depth: u32,
 }
 
-impl<'tcx> FnCtxt<'_, 'tcx> {
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
         let code = ObligationCauseCode::Pattern {
             span: ti.span,
@@ -100,12 +101,12 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     }
 
     fn demand_eqtype_pat_diag(
-        &self,
+        &'a self,
         cause_span: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         ti: &TopInfo<'tcx>,
-    ) -> Result<(), Diag<'tcx>> {
+    ) -> Result<(), Diag<'a>> {
         self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
             .map_err(|mut diag| {
                 if let Some(expr) = ti.origin_expr {
@@ -328,8 +329,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         adjust_mode: AdjustMode,
         max_ref_mutbl: MutblCap,
     ) -> (Ty<'tcx>, ByRef, MutblCap) {
-        if let ByRef::Yes(Mutability::Mut) = def_br {
-            debug_assert!(max_ref_mutbl == MutblCap::Mut);
+        #[cfg(debug_assertions)]
+        if def_br == ByRef::Yes(Mutability::Mut) && max_ref_mutbl != MutblCap::Mut {
+            span_bug!(pat.span, "Pattern mutability cap violated!");
         }
         match adjust_mode {
             AdjustMode::Pass => (expected, def_br, max_ref_mutbl),
@@ -437,7 +439,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             });
         }
 
-        if self.tcx.features().ref_pat_eat_one_layer_2024 {
+        let features = self.tcx.features();
+        if features.ref_pat_eat_one_layer_2024 || features.ref_pat_eat_one_layer_2024_structural {
             def_br = def_br.cap_ref_mutability(max_ref_mutbl.as_mutbl());
             if def_br == ByRef::Yes(Mutability::Not) {
                 max_ref_mutbl = MutblCap::Not;
@@ -669,7 +672,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Determine the binding mode...
         let bm = match user_bind_annot {
             BindingMode(ByRef::No, Mutability::Mut) if matches!(def_br, ByRef::Yes(_)) => {
-                if pat.span.at_least_rust_2024() && self.tcx.features().ref_pat_eat_one_layer_2024 {
+                if pat.span.at_least_rust_2024()
+                    && (self.tcx.features().ref_pat_eat_one_layer_2024
+                        || self.tcx.features().ref_pat_eat_one_layer_2024_structural)
+                {
                     if !self.tcx.features().mut_ref {
                         feature_err(
                             &self.tcx.sess,
@@ -698,7 +704,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             && let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl
         {
             let mut err = struct_span_code_err!(
-                self.tcx.dcx(),
+                self.dcx(),
                 ident.span,
                 E0596,
                 "cannot borrow as mutable inside an `&` pattern"
@@ -1010,7 +1016,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let (res, opt_ty, segments) = path_resolution;
         match res {
             Res::Err => {
-                let e = tcx.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
+                let e =
+                    self.dcx().span_delayed_bug(qpath.span(), "`Res::Err` but no error emitted");
                 self.set_tainted_by_errors(e);
                 return Ty::new_error(tcx, e);
             }
@@ -1191,7 +1198,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let (res, opt_ty, segments) =
             self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span);
         if res == Res::Err {
-            let e = tcx.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted");
+            let e = self.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted");
             self.set_tainted_by_errors(e);
             on_error(e);
             return Ty::new_error(tcx, e);
@@ -1207,7 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let variant = match res {
             Res::Err => {
-                tcx.dcx().span_bug(pat.span, "`Res::Err` but no error emitted");
+                self.dcx().span_bug(pat.span, "`Res::Err` but no error emitted");
             }
             Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => {
                 let e = report_unexpected_res(res);
@@ -1549,10 +1556,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Report an error if an incorrect number of fields was specified.
         if adt.is_union() {
             if fields.len() != 1 {
-                tcx.dcx().emit_err(errors::UnionPatMultipleFields { span: pat.span });
+                self.dcx().emit_err(errors::UnionPatMultipleFields { span: pat.span });
             }
             if has_rest_pat {
-                tcx.dcx().emit_err(errors::UnionPatDotDot { span: pat.span });
+                self.dcx().emit_err(errors::UnionPatDotDot { span: pat.span });
             }
         } else if !unmentioned_fields.is_empty() {
             let accessible_unmentioned_fields: Vec<_> = unmentioned_fields
@@ -1690,7 +1697,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx Pat<'tcx>,
         variant: &ty::VariantDef,
         args: ty::GenericArgsRef<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let tcx = self.tcx;
         let (field_names, t, plural) = if let [field] = inexistent_fields {
             (format!("a field named `{}`", field.ident), "this", "")
@@ -1710,7 +1717,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::<Vec<_>>();
         let mut err = struct_span_code_err!(
-            tcx.dcx(),
+            self.dcx(),
             spans,
             E0026,
             "{} `{}` does not have {}",
@@ -1881,7 +1888,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let mut err = self
             .dcx()
             .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields");
@@ -1973,7 +1980,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         unmentioned_fields: &[(&ty::FieldDef, Ident)],
         have_inaccessible_fields: bool,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" };
         let field_names = if let [(_, field)] = unmentioned_fields {
             format!("field `{field}`{inaccessible}")
@@ -2122,7 +2129,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         mut expected: Ty<'tcx>,
         mut pat_info: PatInfo<'tcx, '_>,
     ) -> Ty<'tcx> {
-        let no_ref_mut_behind_and = self.tcx.features().ref_pat_eat_one_layer_2024;
+        let tcx = self.tcx;
+        let features = tcx.features();
+        let ref_pat_eat_one_layer_2024 = features.ref_pat_eat_one_layer_2024;
+        let ref_pat_eat_one_layer_2024_structural = features.ref_pat_eat_one_layer_2024_structural;
+
+        let no_ref_mut_behind_and =
+            ref_pat_eat_one_layer_2024 || ref_pat_eat_one_layer_2024_structural;
         let new_match_ergonomics = pat.span.at_least_rust_2024() && no_ref_mut_behind_and;
 
         let pat_prefix_span =
@@ -2137,32 +2150,49 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             pat_info.max_ref_mutbl = MutblCap::Mut;
         }
 
+        expected = self.try_structurally_resolve_type(pat.span, expected);
         if new_match_ergonomics {
             if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
-                // ref pattern consumes inherited reference
-
-                if pat_mutbl > inh_mut {
-                    // Tried to match inherited `ref` with `&mut`, which is an error
-                    let err_msg = "cannot match inherited `&` with `&mut` pattern";
-                    let err = if let Some(span) = pat_prefix_span {
-                        let mut err = self.dcx().struct_span_err(span, err_msg);
-                        err.span_suggestion_verbose(
-                            span,
-                            "replace this `&mut` pattern with `&`",
-                            "&",
-                            Applicability::MachineApplicable,
-                        );
-                        err
+                if !ref_pat_eat_one_layer_2024 && let ty::Ref(_, _, r_mutbl) = *expected.kind() {
+                    // Don't attempt to consume inherited reference
+                    pat_info.binding_mode = pat_info.binding_mode.cap_ref_mutability(r_mutbl);
+                } else {
+                    // ref pattern attempts to consume inherited reference
+                    if pat_mutbl > inh_mut {
+                        // Tried to match inherited `ref` with `&mut`
+                        if !ref_pat_eat_one_layer_2024_structural {
+                            let err_msg = "mismatched types";
+                            let err = if let Some(span) = pat_prefix_span {
+                                let mut err = self.dcx().struct_span_err(span, err_msg);
+                                err.code(E0308);
+                                err.note("cannot match inherited `&` with `&mut` pattern");
+                                err.span_suggestion_verbose(
+                                    span,
+                                    "replace this `&mut` pattern with `&`",
+                                    "&",
+                                    Applicability::MachineApplicable,
+                                );
+                                err
+                            } else {
+                                self.dcx().struct_span_err(pat.span, err_msg)
+                            };
+                            err.emit();
+
+                            pat_info.binding_mode = ByRef::No;
+                            self.typeck_results
+                                .borrow_mut()
+                                .skipped_ref_pats_mut()
+                                .insert(pat.hir_id);
+                            self.check_pat(inner, expected, pat_info);
+                            return expected;
+                        }
                     } else {
-                        self.dcx().struct_span_err(pat.span, err_msg)
-                    };
-                    err.emit();
+                        pat_info.binding_mode = ByRef::No;
+                        self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
+                        self.check_pat(inner, expected, pat_info);
+                        return expected;
+                    }
                 }
-
-                pat_info.binding_mode = ByRef::No;
-                self.typeck_results.borrow_mut().skipped_ref_pats_mut().insert(pat.hir_id);
-                self.check_pat(inner, expected, pat_info);
-                return expected;
             }
         } else {
             // Reset binding mode on old editions
@@ -2177,8 +2207,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        let tcx = self.tcx;
-        expected = self.try_structurally_resolve_type(pat.span, expected);
         let (ref_ty, inner_ty) = match self.check_dereferenceable(pat.span, expected, inner) {
             Ok(()) => {
                 // `demand::subtype` would be good enough, but using `eqtype` turns
@@ -2190,7 +2218,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 debug!("check_pat_ref: expected={:?}", expected);
                 match *expected.kind() {
                     ty::Ref(_, r_ty, r_mutbl)
-                        if (new_match_ergonomics && r_mutbl >= pat_mutbl)
+                        if (no_ref_mut_behind_and && r_mutbl >= pat_mutbl)
                             || r_mutbl == pat_mutbl =>
                     {
                         if no_ref_mut_behind_and && r_mutbl == Mutability::Not {
@@ -2498,7 +2526,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..)))
             && let Some(span) = ti.span
             && let Some(_) = ti.origin_expr
-            && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
         {
             let resolved_ty = self.resolve_vars_if_possible(ti.expected);
             let (is_slice_or_array_or_vector, resolved_ty) =
@@ -2509,10 +2536,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) =>
                 {
                     // Slicing won't work here, but `.as_deref()` might (issue #91328).
-                    err.span_suggestion(
-                        span,
+                    err.span_suggestion_verbose(
+                        span.shrink_to_hi(),
                         "consider using `as_deref` here",
-                        format!("{snippet}.as_deref()"),
+                        ".as_deref()",
                         Applicability::MaybeIncorrect,
                     );
                 }
@@ -2521,10 +2548,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let is_top_level = current_depth <= 1;
             if is_slice_or_array_or_vector && is_top_level {
-                err.span_suggestion(
-                    span,
+                err.span_suggestion_verbose(
+                    span.shrink_to_hi(),
                     "consider slicing here",
-                    format!("{snippet}[..]"),
+                    "[..]",
                     Applicability::MachineApplicable,
                 );
             }
diff --git a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
index b6e9000ef95..c99e8a7fe8e 100644
--- a/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
+++ b/compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs
@@ -18,7 +18,7 @@ use rustc_trait_selection::traits::{
 use std::cell::RefCell;
 use std::ops::Deref;
 
-// Data shared between a "typeck root" and its nested bodies,
+/// Data shared between a "typeck root" and its nested bodies,
 /// e.g. closures defined within the function. For example:
 /// ```ignore (illustrative)
 /// fn foo() {
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index aea0114167e..e800c1a97d9 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -18,8 +18,8 @@ use rustc_middle::ty::TypeSuperFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::solve;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 
 use std::mem;
 
@@ -219,28 +219,9 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
     fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) {
         if let hir::ExprKind::Index(ref base, ref index, _) = e.kind {
             // All valid indexing looks like this; might encounter non-valid indexes at this point.
-            let base_ty = self.typeck_results.expr_ty_adjusted_opt(base);
-            if base_ty.is_none() {
-                // When encountering `return [0][0]` outside of a `fn` body we can encounter a base
-                // that isn't in the type table. We assume more relevant errors have already been
-                // emitted. (#64638)
-                assert!(self.tcx().dcx().has_errors().is_some(), "bad base: `{base:?}`");
-            }
-            if let Some(base_ty) = base_ty
-                && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind()
-            {
-                let index_ty =
-                    self.typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| {
-                        // When encountering `return [0][0]` outside of a `fn` body we would attempt
-                        // to access an nonexistent index. We assume that more relevant errors will
-                        // already have been emitted, so we only gate on this with an ICE if no
-                        // error has been emitted. (#64638)
-                        Ty::new_error_with_message(
-                            self.fcx.tcx,
-                            e.span,
-                            format!("bad index {index:?} for base: `{base:?}`"),
-                        )
-                    });
+            let base_ty = self.typeck_results.expr_ty_adjusted(base);
+            if let ty::Ref(_, base_ty_inner, _) = *base_ty.kind() {
+                let index_ty = self.typeck_results.expr_ty_adjusted(index);
                 if self.is_builtin_index(e, base_ty_inner, index_ty) {
                     // Remove the method call record
                     self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
diff --git a/compiler/rustc_index/src/lib.rs b/compiler/rustc_index/src/lib.rs
index db6b250467e..b775ae1f5e9 100644
--- a/compiler/rustc_index/src/lib.rs
+++ b/compiler/rustc_index/src/lib.rs
@@ -1,10 +1,7 @@
 // tidy-alphabetical-start
 #![cfg_attr(all(feature = "nightly", test), feature(stmt_expr_attributes))]
-#![cfg_attr(
-    feature = "nightly",
-    feature(extend_one, min_specialization, new_uninit, step_trait, test)
-)]
 #![cfg_attr(feature = "nightly", allow(internal_features))]
+#![cfg_attr(feature = "nightly", feature(extend_one, new_uninit, step_trait, test))]
 // tidy-alphabetical-end
 
 pub mod bit_set;
diff --git a/compiler/rustc_index/src/vec.rs b/compiler/rustc_index/src/vec.rs
index 88298150a79..b866c8b8433 100644
--- a/compiler/rustc_index/src/vec.rs
+++ b/compiler/rustc_index/src/vec.rs
@@ -208,6 +208,11 @@ impl<I: Idx, T> IndexVec<I, Option<T>> {
     pub fn remove(&mut self, index: I) -> Option<T> {
         self.get_mut(index)?.take()
     }
+
+    #[inline]
+    pub fn contains(&self, index: I) -> bool {
+        self.get(index).and_then(Option::as_ref).is_some()
+    }
 }
 
 impl<I: Idx, T: fmt::Debug> fmt::Debug for IndexVec<I, T> {
diff --git a/compiler/rustc_index_macros/src/lib.rs b/compiler/rustc_index_macros/src/lib.rs
index 3e55dd82a6e..2b444932f85 100644
--- a/compiler/rustc_index_macros/src/lib.rs
+++ b/compiler/rustc_index_macros/src/lib.rs
@@ -34,10 +34,7 @@ mod newtype;
 ///   optimizations. The default max value is 0xFFFF_FF00.
 /// - `#[gate_rustc_only]`: makes parts of the generated code nightly-only.
 #[proc_macro]
-#[cfg_attr(
-    feature = "nightly",
-    allow_internal_unstable(step_trait, rustc_attrs, trusted_step, min_specialization)
-)]
+#[cfg_attr(feature = "nightly", allow_internal_unstable(step_trait, rustc_attrs, trusted_step))]
 pub fn newtype_index(input: TokenStream) -> TokenStream {
     newtype::newtype(input)
 }
diff --git a/compiler/rustc_index_macros/src/newtype.rs b/compiler/rustc_index_macros/src/newtype.rs
index 41863f7b15f..1ac2c44e9dc 100644
--- a/compiler/rustc_index_macros/src/newtype.rs
+++ b/compiler/rustc_index_macros/src/newtype.rs
@@ -139,10 +139,6 @@ impl Parse for Newtype {
                         Self::index(start).checked_sub(u).map(Self::from_usize)
                     }
                 }
-
-                // Safety: The implementation of `Step` upholds all invariants.
-                #gate_rustc_only
-                unsafe impl ::std::iter::TrustedStep for #name {}
             }
         } else {
             quote! {}
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml
index c1565a7d40f..1f616710200 100644
--- a/compiler/rustc_infer/Cargo.toml
+++ b/compiler/rustc_infer/Cargo.toml
@@ -16,8 +16,10 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
+rustc_next_trait_solver = { path = "../rustc_next_trait_solver" }
 rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
+rustc_type_ir = { path = "../rustc_type_ir" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_infer/src/infer/context.rs b/compiler/rustc_infer/src/infer/context.rs
new file mode 100644
index 00000000000..f35a8162d96
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/context.rs
@@ -0,0 +1,176 @@
+///! Definition of `InferCtxtLike` from the librarified type layer.
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::traits::solve::{Goal, NoSolution, SolverMode};
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::fold::TypeFoldable;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+use rustc_type_ir::relate::Relate;
+use rustc_type_ir::InferCtxtLike;
+
+use super::{BoundRegionConversionTime, InferCtxt, SubregionOrigin};
+
+impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
+    type Interner = TyCtxt<'tcx>;
+
+    fn cx(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn solver_mode(&self) -> ty::solve::SolverMode {
+        match self.intercrate {
+            true => SolverMode::Coherence,
+            false => SolverMode::Normal,
+        }
+    }
+
+    fn universe(&self) -> ty::UniverseIndex {
+        self.universe()
+    }
+
+    fn create_next_universe(&self) -> ty::UniverseIndex {
+        self.create_next_universe()
+    }
+
+    fn universe_of_ty(&self, vid: ty::TyVid) -> Option<ty::UniverseIndex> {
+        match self.probe_ty_var(vid) {
+            Err(universe) => Some(universe),
+            Ok(_) => None,
+        }
+    }
+
+    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
+        match self.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) {
+            Err(universe) => Some(universe),
+            Ok(_) => None,
+        }
+    }
+
+    fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex> {
+        match self.probe_const_var(ct) {
+            Err(universe) => Some(universe),
+            Ok(_) => None,
+        }
+    }
+
+    fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid {
+        self.root_var(var)
+    }
+
+    fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
+        self.root_const_var(var)
+    }
+
+    fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> {
+        match self.probe_ty_var(vid) {
+            Ok(ty) => ty,
+            Err(_) => Ty::new_var(self.tcx, self.root_var(vid)),
+        }
+    }
+
+    fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> {
+        self.opportunistic_resolve_int_var(vid)
+    }
+
+    fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> {
+        self.opportunistic_resolve_float_var(vid)
+    }
+
+    fn opportunistic_resolve_ct_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> {
+        match self.probe_const_var(vid) {
+            Ok(ct) => ct,
+            Err(_) => ty::Const::new_var(self.tcx, self.root_const_var(vid)),
+        }
+    }
+
+    fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> {
+        match self.probe_effect_var(vid) {
+            Some(ct) => ct,
+            None => {
+                ty::Const::new_infer(self.tcx, ty::InferConst::EffectVar(self.root_effect_var(vid)))
+            }
+        }
+    }
+
+    fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> {
+        self.inner.borrow_mut().unwrap_region_constraints().opportunistic_resolve_var(self.tcx, vid)
+    }
+
+    fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
+        self.defining_opaque_types()
+    }
+
+    fn next_ty_infer(&self) -> Ty<'tcx> {
+        self.next_ty_var(DUMMY_SP)
+    }
+
+    fn next_const_infer(&self) -> ty::Const<'tcx> {
+        self.next_const_var(DUMMY_SP)
+    }
+
+    fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
+        self.fresh_args_for_item(DUMMY_SP, def_id)
+    }
+
+    fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+    ) -> T {
+        self.instantiate_binder_with_fresh_vars(
+            DUMMY_SP,
+            BoundRegionConversionTime::HigherRankedType,
+            value,
+        )
+    }
+
+    fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
+        &self,
+        value: ty::Binder<'tcx, T>,
+        f: impl FnOnce(T) -> U,
+    ) -> U {
+        self.enter_forall(value, f)
+    }
+
+    fn relate<T: Relate<TyCtxt<'tcx>>>(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        self.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs)
+    }
+
+    fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        lhs: T,
+        rhs: T,
+    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
+        self.at(&ObligationCause::dummy(), param_env)
+            .eq_structurally_relating_aliases_no_trace(lhs, rhs)
+    }
+
+    fn shallow_resolve(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        self.shallow_resolve(ty)
+    }
+
+    fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        self.resolve_vars_if_possible(value)
+    }
+
+    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T {
+        self.probe(|_| probe())
+    }
+
+    fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
+        self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup)
+    }
+
+    fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {
+        self.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy());
+    }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 227691d0994..bb1285ee813 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -45,13 +45,10 @@
 //! ported to this system, and which relies on string concatenation at the
 //! time of error detection.
 
-use super::lexical_region_resolve::RegionResolutionError;
-use super::region_constraints::GenericKind;
-use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
+use super::{InferCtxt, TypeTrace, ValuePairs};
 
-use crate::errors::{self, ObligationCauseFailureCode, TypeErrorAdditionalDiags};
+use crate::errors::{ObligationCauseFailureCode, TypeErrorAdditionalDiags};
 use crate::infer;
-use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
 use crate::infer::ExpectedFound;
 use crate::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -61,38 +58,36 @@ use crate::traits::{
 use crate::infer::relate::{self, RelateResult, TypeRelation};
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle,
-    DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart,
+    pluralize, Applicability, Diag, DiagCtxtHandle, DiagStyledString, IntoDiagArg, StringPart,
 };
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::lang_items::LangItem;
-use rustc_hir::{self as hir, ParamName};
+use rustc_hir::{self as hir};
 use rustc_macros::extension;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::DepContext;
 use rustc_middle::ty::error::TypeErrorToStringExt;
 use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError, PrintTraitRefExt as _};
-use rustc_middle::ty::Upcast;
 use rustc_middle::ty::{
-    self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
-    TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
+    self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+    TypeVisitable, TypeVisitableExt,
 };
-use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
+use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
 use rustc_target::spec::abi;
 use std::borrow::Cow;
 use std::ops::{ControlFlow, Deref};
 use std::path::PathBuf;
 use std::{cmp, fmt, iter};
 
-mod note;
 mod note_and_explain;
 mod suggest;
 
 pub(crate) mod need_type_info;
 pub mod sub_relations;
 pub use need_type_info::TypeAnnotationNeeded;
+pub mod region;
 
 pub mod nice_region_error;
 
@@ -139,7 +134,7 @@ pub struct TypeErrCtxt<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
-    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
+    pub fn dcx(&self) -> DiagCtxtHandle<'a> {
         self.infcx.dcx()
     }
 
@@ -159,244 +154,6 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
     }
 }
 
-pub(super) fn note_and_explain_region<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    err: &mut Diag<'_>,
-    generic_param_scope: LocalDefId,
-    prefix: &str,
-    region: ty::Region<'tcx>,
-    suffix: &str,
-    alt_span: Option<Span>,
-) {
-    let (description, span) = match *region {
-        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
-            msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
-        }
-
-        ty::ReError(_) => return,
-
-        // FIXME(#125431): `ReVar` shouldn't reach here.
-        ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
-
-        ty::ReBound(..) | ty::ReErased => {
-            bug!("unexpected region for note_and_explain_region: {:?}", region);
-        }
-    };
-
-    emit_msg_span(err, prefix, description, span, suffix);
-}
-
-fn explain_free_region<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    err: &mut Diag<'_>,
-    generic_param_scope: LocalDefId,
-    prefix: &str,
-    region: ty::Region<'tcx>,
-    suffix: &str,
-) {
-    let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
-
-    label_msg_span(err, prefix, description, span, suffix);
-}
-
-fn msg_span_from_named_region<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    generic_param_scope: LocalDefId,
-    region: ty::Region<'tcx>,
-    alt_span: Option<Span>,
-) -> (String, Option<Span>) {
-    match *region {
-        ty::ReEarlyParam(br) => {
-            let scope = tcx
-                .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
-                .expect_local();
-            let span = if let Some(param) =
-                tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
-            {
-                param.span
-            } else {
-                tcx.def_span(scope)
-            };
-            let text = if br.has_name() {
-                format!("the lifetime `{}` as defined here", br.name)
-            } else {
-                "the anonymous lifetime as defined here".to_string()
-            };
-            (text, Some(span))
-        }
-        ty::ReLateParam(ref fr) => {
-            if !fr.bound_region.is_named()
-                && let Some((ty, _)) =
-                    find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
-            {
-                ("the anonymous lifetime defined here".to_string(), Some(ty.span))
-            } else {
-                match fr.bound_region {
-                    ty::BoundRegionKind::BrNamed(_, name) => {
-                        let span = if let Some(param) = tcx
-                            .hir()
-                            .get_generics(generic_param_scope)
-                            .and_then(|generics| generics.get_named(name))
-                        {
-                            param.span
-                        } else {
-                            tcx.def_span(generic_param_scope)
-                        };
-                        let text = if name == kw::UnderscoreLifetime {
-                            "the anonymous lifetime as defined here".to_string()
-                        } else {
-                            format!("the lifetime `{name}` as defined here")
-                        };
-                        (text, Some(span))
-                    }
-                    ty::BrAnon => (
-                        "the anonymous lifetime as defined here".to_string(),
-                        Some(tcx.def_span(generic_param_scope)),
-                    ),
-                    _ => (
-                        format!("the lifetime `{region}` as defined here"),
-                        Some(tcx.def_span(generic_param_scope)),
-                    ),
-                }
-            }
-        }
-        ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
-        ty::RePlaceholder(ty::PlaceholderRegion {
-            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
-            ..
-        }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
-        ty::RePlaceholder(ty::PlaceholderRegion {
-            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
-            ..
-        }) => ("an anonymous lifetime".to_owned(), None),
-        _ => bug!("{:?}", region),
-    }
-}
-
-fn emit_msg_span(
-    err: &mut Diag<'_>,
-    prefix: &str,
-    description: String,
-    span: Option<Span>,
-    suffix: &str,
-) {
-    let message = format!("{prefix}{description}{suffix}");
-
-    if let Some(span) = span {
-        err.span_note(span, message);
-    } else {
-        err.note(message);
-    }
-}
-
-fn label_msg_span(
-    err: &mut Diag<'_>,
-    prefix: &str,
-    description: String,
-    span: Option<Span>,
-    suffix: &str,
-) {
-    let message = format!("{prefix}{description}{suffix}");
-
-    if let Some(span) = span {
-        err.span_label(span, message);
-    } else {
-        err.note(message);
-    }
-}
-
-#[instrument(level = "trace", skip(tcx))]
-pub fn unexpected_hidden_region_diagnostic<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    generic_param_scope: LocalDefId,
-    span: Span,
-    hidden_ty: Ty<'tcx>,
-    hidden_region: ty::Region<'tcx>,
-    opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
-) -> Diag<'tcx> {
-    let mut err = tcx.dcx().create_err(errors::OpaqueCapturesLifetime {
-        span,
-        opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
-        opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
-    });
-
-    // Explain the region we are capturing.
-    match *hidden_region {
-        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
-            // Assuming regionck succeeded (*), we ought to always be
-            // capturing *some* region from the fn header, and hence it
-            // ought to be free. So under normal circumstances, we will go
-            // down this path which gives a decent human readable
-            // explanation.
-            //
-            // (*) if not, the `tainted_by_errors` field would be set to
-            // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all.
-            explain_free_region(
-                tcx,
-                &mut err,
-                generic_param_scope,
-                &format!("hidden type `{hidden_ty}` captures "),
-                hidden_region,
-                "",
-            );
-            if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
-                let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
-                nice_region_error::suggest_new_region_bound(
-                    tcx,
-                    &mut err,
-                    fn_returns,
-                    hidden_region.to_string(),
-                    None,
-                    format!("captures `{hidden_region}`"),
-                    None,
-                    Some(reg_info.def_id),
-                )
-            }
-        }
-        ty::RePlaceholder(_) => {
-            explain_free_region(
-                tcx,
-                &mut err,
-                generic_param_scope,
-                &format!("hidden type `{}` captures ", hidden_ty),
-                hidden_region,
-                "",
-            );
-        }
-        ty::ReError(_) => {
-            err.downgrade_to_delayed_bug();
-        }
-        _ => {
-            // Ugh. This is a painful case: the hidden region is not one
-            // that we can easily summarize or explain. This can happen
-            // in a case like
-            // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
-            //
-            // ```
-            // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
-            //   if condition() { a } else { b }
-            // }
-            // ```
-            //
-            // Here the captured lifetime is the intersection of `'a` and
-            // `'b`, which we can't quite express.
-
-            // We can at least report a really cryptic error for now.
-            note_and_explain_region(
-                tcx,
-                &mut err,
-                generic_param_scope,
-                &format!("hidden type `{hidden_ty}` captures "),
-                hidden_region,
-                "",
-                None,
-            );
-        }
-    }
-
-    err
-}
-
 impl<'tcx> InferCtxt<'tcx> {
     pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
         let (def_id, args) = match *ty.kind() {
@@ -436,194 +193,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
-    pub fn report_region_errors(
-        &self,
-        generic_param_scope: LocalDefId,
-        errors: &[RegionResolutionError<'tcx>],
-    ) -> ErrorGuaranteed {
-        assert!(!errors.is_empty());
-
-        if let Some(guaranteed) = self.infcx.tainted_by_errors() {
-            return guaranteed;
-        }
-
-        debug!("report_region_errors(): {} errors to start", errors.len());
-
-        // try to pre-process the errors, which will group some of them
-        // together into a `ProcessedErrors` group:
-        let errors = self.process_errors(errors);
-
-        debug!("report_region_errors: {} errors after preprocessing", errors.len());
-
-        let mut guar = None;
-        for error in errors {
-            debug!("report_region_errors: error = {:?}", error);
-
-            let e = if let Some(guar) =
-                self.try_report_nice_region_error(generic_param_scope, &error)
-            {
-                guar
-            } else {
-                match error.clone() {
-                    // These errors could indicate all manner of different
-                    // problems with many different solutions. Rather
-                    // than generate a "one size fits all" error, what we
-                    // attempt to do is go through a number of specific
-                    // scenarios and try to find the best way to present
-                    // the error. If all of these fails, we fall back to a rather
-                    // general bit of code that displays the error information
-                    RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
-                        if sub.is_placeholder() || sup.is_placeholder() {
-                            self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
-                                .emit()
-                        } else {
-                            self.report_concrete_failure(generic_param_scope, origin, sub, sup)
-                                .emit()
-                        }
-                    }
-
-                    RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
-                        .report_generic_bound_failure(
-                            generic_param_scope,
-                            origin.span(),
-                            Some(origin),
-                            param_ty,
-                            sub,
-                        ),
-
-                    RegionResolutionError::SubSupConflict(
-                        _,
-                        var_origin,
-                        sub_origin,
-                        sub_r,
-                        sup_origin,
-                        sup_r,
-                        _,
-                    ) => {
-                        if sub_r.is_placeholder() {
-                            self.report_placeholder_failure(
-                                generic_param_scope,
-                                sub_origin,
-                                sub_r,
-                                sup_r,
-                            )
-                            .emit()
-                        } else if sup_r.is_placeholder() {
-                            self.report_placeholder_failure(
-                                generic_param_scope,
-                                sup_origin,
-                                sub_r,
-                                sup_r,
-                            )
-                            .emit()
-                        } else {
-                            self.report_sub_sup_conflict(
-                                generic_param_scope,
-                                var_origin,
-                                sub_origin,
-                                sub_r,
-                                sup_origin,
-                                sup_r,
-                            )
-                        }
-                    }
-
-                    RegionResolutionError::UpperBoundUniverseConflict(
-                        _,
-                        _,
-                        _,
-                        sup_origin,
-                        sup_r,
-                    ) => {
-                        assert!(sup_r.is_placeholder());
-
-                        // Make a dummy value for the "sub region" --
-                        // this is the initial value of the
-                        // placeholder. In practice, we expect more
-                        // tailored errors that don't really use this
-                        // value.
-                        let sub_r = self.tcx.lifetimes.re_erased;
-
-                        self.report_placeholder_failure(
-                            generic_param_scope,
-                            sup_origin,
-                            sub_r,
-                            sup_r,
-                        )
-                        .emit()
-                    }
-
-                    RegionResolutionError::CannotNormalize(clause, origin) => {
-                        let clause: ty::Clause<'tcx> =
-                            clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
-                        self.tcx
-                            .dcx()
-                            .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
-                            .emit()
-                    }
-                }
-            };
-
-            guar = Some(e)
-        }
-
-        guar.unwrap()
-    }
-
-    // This method goes through all the errors and try to group certain types
-    // of error together, for the purpose of suggesting explicit lifetime
-    // parameters to the user. This is done so that we can have a more
-    // complete view of what lifetimes should be the same.
-    // If the return value is an empty vector, it means that processing
-    // failed (so the return value of this method should not be used).
-    //
-    // The method also attempts to weed out messages that seem like
-    // duplicates that will be unhelpful to the end-user. But
-    // obviously it never weeds out ALL errors.
-    fn process_errors(
-        &self,
-        errors: &[RegionResolutionError<'tcx>],
-    ) -> Vec<RegionResolutionError<'tcx>> {
-        debug!("process_errors()");
-
-        // We want to avoid reporting generic-bound failures if we can
-        // avoid it: these have a very high rate of being unhelpful in
-        // practice. This is because they are basically secondary
-        // checks that test the state of the region graph after the
-        // rest of inference is done, and the other kinds of errors
-        // indicate that the region constraint graph is internally
-        // inconsistent, so these test results are likely to be
-        // meaningless.
-        //
-        // Therefore, we filter them out of the list unless they are
-        // the only thing in the list.
-
-        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
-            RegionResolutionError::GenericBoundFailure(..) => true,
-            RegionResolutionError::ConcreteFailure(..)
-            | RegionResolutionError::SubSupConflict(..)
-            | RegionResolutionError::UpperBoundUniverseConflict(..)
-            | RegionResolutionError::CannotNormalize(..) => false,
-        };
-
-        let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
-            errors.to_owned()
-        } else {
-            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
-        };
-
-        // sort the errors by span, for better error message stability.
-        errors.sort_by_key(|u| match *u {
-            RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
-            RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
-            RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
-            RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
-            RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
-        });
-        errors
-    }
-
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// Adds a note if the types come from similarly named crates
     fn check_and_note_conflicting_crates(&self, err: &mut Diag<'_>, terr: TypeError<'tcx>) {
         use hir::def_id::CrateNum;
@@ -1167,14 +737,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let output1 = sig1.output();
         let output2 = sig2.output();
         let (x1, x2) = self.cmp(output1, output2);
-        if !output1.is_unit() {
+        let output_diff = x1 != x2;
+        if !output1.is_unit() || output_diff {
             values.0.push_normal(" -> ");
             (values.0).0.extend(x1.0);
         }
-        if !output2.is_unit() {
+        if !output2.is_unit() || output_diff {
             values.1.push_normal(" -> ");
             (values.1).0.extend(x2.0);
         }
+
         values
     }
 
@@ -2206,7 +1778,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         trace: TypeTrace<'tcx>,
         terr: TypeError<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
 
         let span = trace.cause.span();
@@ -2215,7 +1787,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             span,
             self.type_error_additional_suggestions(&trace, terr),
         );
-        let mut diag = self.tcx.dcx().create_err(failure_code);
+        let mut diag = self.dcx().create_err(failure_code);
         self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false);
         diag
     }
@@ -2338,359 +1910,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         ))
     }
 
-    pub fn report_generic_bound_failure(
-        &self,
-        generic_param_scope: LocalDefId,
-        span: Span,
-        origin: Option<SubregionOrigin<'tcx>>,
-        bound_kind: GenericKind<'tcx>,
-        sub: Region<'tcx>,
-    ) -> ErrorGuaranteed {
-        self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
-            .emit()
-    }
-
-    pub fn construct_generic_bound_failure(
-        &self,
-        generic_param_scope: LocalDefId,
-        span: Span,
-        origin: Option<SubregionOrigin<'tcx>>,
-        bound_kind: GenericKind<'tcx>,
-        sub: Region<'tcx>,
-    ) -> Diag<'tcx> {
-        if let Some(SubregionOrigin::CompareImplItemObligation {
-            span,
-            impl_item_def_id,
-            trait_item_def_id,
-        }) = origin
-        {
-            return self.report_extra_impl_obligation(
-                span,
-                impl_item_def_id,
-                trait_item_def_id,
-                &format!("`{bound_kind}: {sub}`"),
-            );
-        }
-
-        let labeled_user_string = match bound_kind {
-            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::Projection | ty::Inherent => {
-                    format!("the associated 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(span, format!("{labeled_user_string} may not live long enough"));
-        err.code(match sub.kind() {
-            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
-            ty::ReStatic => E0310,
-            _ => E0311,
-        });
-
-        '_explain: {
-            let (description, span) = match sub.kind() {
-                ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
-                    msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
-                }
-                _ => (format!("lifetime `{sub}`"), Some(span)),
-            };
-            let prefix = format!("{labeled_user_string} must be valid for ");
-            label_msg_span(&mut err, &prefix, description, span, "...");
-            if let Some(origin) = origin {
-                self.note_region_origin(&mut err, &origin);
-            }
-        }
-
-        'suggestion: {
-            let msg = "consider adding an explicit lifetime bound";
-
-            if (bound_kind, sub).has_infer_regions()
-                || (bound_kind, sub).has_placeholders()
-                || !bound_kind.is_suggestable(self.tcx, false)
-            {
-                let lt_name = sub.get_name_or_anon().to_string();
-                err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
-                break 'suggestion;
-            }
-
-            let mut generic_param_scope = generic_param_scope;
-            while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
-                generic_param_scope = self.tcx.local_parent(generic_param_scope);
-            }
-
-            // type_param_sugg_span is (span, has_bounds, needs_parentheses)
-            let (type_scope, type_param_sugg_span) = match bound_kind {
-                GenericKind::Param(param) => {
-                    let generics = self.tcx.generics_of(generic_param_scope);
-                    let type_param = generics.type_param(param, self.tcx);
-                    let def_id = type_param.def_id.expect_local();
-                    let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
-                    // Get the `hir::Param` to verify whether it already has any bounds.
-                    // We do this to avoid suggesting code that ends up as `T: 'a'b`,
-                    // instead we suggest `T: 'a + 'b` in that case.
-                    let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
-                    let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
-                        Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
-                        // If `param` corresponds to `Self`, no usable suggestion span.
-                        None if generics.has_self && param.index == 0 => None,
-                        None => {
-                            let span = if let Some(param) =
-                                hir_generics.params.iter().find(|param| param.def_id == def_id)
-                                && let ParamName::Plain(ident) = param.name
-                            {
-                                ident.span.shrink_to_hi()
-                            } else {
-                                let span = self.tcx.def_span(def_id);
-                                span.shrink_to_hi()
-                            };
-                            Some((span, false, None))
-                        }
-                    };
-                    (scope, sugg_span)
-                }
-                _ => (generic_param_scope, None),
-            };
-            let suggestion_scope = {
-                let lifetime_scope = match sub.kind() {
-                    ty::ReStatic => hir::def_id::CRATE_DEF_ID,
-                    _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
-                        Some(info) => info.def_id,
-                        None => generic_param_scope,
-                    },
-                };
-                match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
-                    true => type_scope,
-                    false => lifetime_scope,
-                }
-            };
-
-            let mut suggs = vec![];
-            let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
-
-            if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
-                && suggestion_scope == type_scope
-            {
-                let suggestion =
-                    if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
-
-                if let Some(open_paren_sp) = open_paren_sp {
-                    suggs.push((open_paren_sp, "(".to_string()));
-                    suggs.push((sp, format!("){suggestion}")));
-                } else {
-                    suggs.push((sp, suggestion))
-                }
-            } else if let GenericKind::Alias(ref p) = bound_kind
-                && let ty::Projection = p.kind(self.tcx)
-                && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
-                && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
-                    self.tcx.opt_rpitit_info(p.def_id)
-            {
-                // The lifetime found in the `impl` is longer than the one on the RPITIT.
-                // Do not suggest `<Type as Trait>::{opaque}: 'static`.
-            } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
-                let pred = format!("{bound_kind}: {lt_name}");
-                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
-                suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
-            } else {
-                let consider = format!("{msg} `{bound_kind}: {sub}`...");
-                err.help(consider);
-            }
-
-            if !suggs.is_empty() {
-                err.multipart_suggestion_verbose(
-                    msg,
-                    suggs,
-                    Applicability::MaybeIncorrect, // Issue #41966
-                );
-            }
-        }
-
-        err
-    }
-
-    pub fn suggest_name_region(
-        &self,
-        generic_param_scope: LocalDefId,
-        lifetime: Region<'tcx>,
-        add_lt_suggs: &mut Vec<(Span, String)>,
-    ) -> String {
-        struct LifetimeReplaceVisitor<'tcx, 'a> {
-            tcx: TyCtxt<'tcx>,
-            needle: hir::LifetimeName,
-            new_lt: &'a str,
-            add_lt_suggs: &'a mut Vec<(Span, String)>,
-        }
-
-        impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
-            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
-                if lt.res == self.needle {
-                    let (pos, span) = lt.suggestion_position();
-                    let new_lt = &self.new_lt;
-                    let sugg = match pos {
-                        hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
-                        hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
-                        hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
-                        hir::LifetimeSuggestionPosition::ElidedPathArgument => {
-                            format!("{new_lt}, ")
-                        }
-                        hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
-                    };
-                    self.add_lt_suggs.push((span, sugg));
-                }
-            }
-
-            fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
-                let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
-                    return hir::intravisit::walk_ty(self, ty);
-                };
-                let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
-                if let Some(&(_, b)) =
-                    opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
-                {
-                    let prev_needle =
-                        std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
-                    for bound in opaque_ty.bounds {
-                        self.visit_param_bound(bound);
-                    }
-                    self.needle = prev_needle;
-                }
-            }
-        }
-
-        let (lifetime_def_id, lifetime_scope) =
-            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
-                Some(info) if !lifetime.has_name() => {
-                    (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
-                }
-                _ => return lifetime.get_name_or_anon().to_string(),
-            };
-
-        let new_lt = {
-            let generics = self.tcx.generics_of(lifetime_scope);
-            let mut used_names =
-                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
-                    .flat_map(|g| &g.own_params)
-                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
-                    .map(|p| p.name)
-                    .collect::<Vec<_>>();
-            let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
-            // consider late-bound lifetimes ...
-            used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
-                |p| match p {
-                    ty::BoundVariableKind::Region(lt) => lt.get_name(),
-                    _ => None,
-                },
-            ));
-            (b'a'..=b'z')
-                .map(|c| format!("'{}", c as char))
-                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
-                .unwrap_or("'lt".to_string())
-        };
-
-        let mut visitor = LifetimeReplaceVisitor {
-            tcx: self.tcx,
-            needle: hir::LifetimeName::Param(lifetime_def_id),
-            add_lt_suggs,
-            new_lt: &new_lt,
-        };
-        match self.tcx.expect_hir_owner_node(lifetime_scope) {
-            hir::OwnerNode::Item(i) => visitor.visit_item(i),
-            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
-            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
-            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
-            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
-            hir::OwnerNode::Synthetic => unreachable!(),
-        }
-
-        let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
-        let sugg = ast_generics
-            .span_for_lifetime_suggestion()
-            .map(|span| (span, format!("{new_lt}, ")))
-            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
-        add_lt_suggs.push(sugg);
-
-        new_lt
-    }
-
-    fn report_sub_sup_conflict(
-        &self,
-        generic_param_scope: LocalDefId,
-        var_origin: RegionVariableOrigin,
-        sub_origin: SubregionOrigin<'tcx>,
-        sub_region: Region<'tcx>,
-        sup_origin: SubregionOrigin<'tcx>,
-        sup_region: Region<'tcx>,
-    ) -> ErrorGuaranteed {
-        let mut err = self.report_inference_failure(var_origin);
-
-        note_and_explain_region(
-            self.tcx,
-            &mut err,
-            generic_param_scope,
-            "first, the lifetime cannot outlive ",
-            sup_region,
-            "...",
-            None,
-        );
-
-        debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
-        debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
-        debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
-        debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
-        debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
-
-        if let infer::Subtype(ref sup_trace) = sup_origin
-            && let infer::Subtype(ref sub_trace) = sub_origin
-            && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
-            && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
-            && sub_expected == sup_expected
-            && sub_found == sup_found
-        {
-            note_and_explain_region(
-                self.tcx,
-                &mut err,
-                generic_param_scope,
-                "...but the lifetime must also be valid for ",
-                sub_region,
-                "...",
-                None,
-            );
-            err.span_note(
-                sup_trace.cause.span,
-                format!("...so that the {}", sup_trace.cause.as_requirement_str()),
-            );
-
-            err.note_expected_found(&"", sup_expected, &"", sup_found);
-            return if sub_region.is_error() | sup_region.is_error() {
-                err.delay_as_bug()
-            } else {
-                err.emit()
-            };
-        }
-
-        self.note_region_origin(&mut err, &sup_origin);
-
-        note_and_explain_region(
-            self.tcx,
-            &mut err,
-            generic_param_scope,
-            "but, the lifetime must be valid for ",
-            sub_region,
-            "...",
-            None,
-        );
-
-        self.note_region_origin(&mut err, &sub_origin);
-        if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
-    }
-
     /// Determine whether an error associated with the given span and definition
     /// should be treated as being caused by the implicit `From` conversion
     /// within `?` desugaring.
@@ -2789,55 +2008,6 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameTypeModuloInfer<'_, 'tcx> {
     }
 }
 
-impl<'tcx> InferCtxt<'tcx> {
-    fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'tcx> {
-        let br_string = |br: ty::BoundRegionKind| {
-            let mut s = match br {
-                ty::BrNamed(_, name) => name.to_string(),
-                _ => String::new(),
-            };
-            if !s.is_empty() {
-                s.push(' ');
-            }
-            s
-        };
-        let var_description = match var_origin {
-            infer::MiscVariable(_) => String::new(),
-            infer::PatternRegion(_) => " for pattern".to_string(),
-            infer::AddrOfRegion(_) => " for borrow expression".to_string(),
-            infer::Autoref(_) => " for autoref".to_string(),
-            infer::Coercion(_) => " for automatic coercion".to_string(),
-            infer::BoundRegion(_, br, infer::FnCall) => {
-                format!(" for lifetime parameter {}in function call", br_string(br))
-            }
-            infer::BoundRegion(_, br, infer::HigherRankedType) => {
-                format!(" for lifetime parameter {}in generic type", br_string(br))
-            }
-            infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
-                " for lifetime parameter {}in trait containing associated type `{}`",
-                br_string(br),
-                self.tcx.associated_item(def_id).name
-            ),
-            infer::RegionParameterDefinition(_, name) => {
-                format!(" for lifetime parameter `{name}`")
-            }
-            infer::UpvarRegion(ref upvar_id, _) => {
-                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
-                format!(" for capture of `{var_name}` by closure")
-            }
-            infer::Nll(..) => bug!("NLL variable found in lexical phase"),
-        };
-
-        struct_span_code_err!(
-            self.tcx.dcx(),
-            var_origin.span(),
-            E0495,
-            "cannot infer an appropriate lifetime{} due to conflicting requirements",
-            var_description
-        )
-    }
-}
-
 pub enum FailureCode {
     Error0317,
     Error0580,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index d7349abc44c..084aebc296f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -391,7 +391,7 @@ impl<'tcx> InferCtxt<'tcx> {
         span: Span,
         arg_data: InferenceDiagnosticsData,
         error_code: TypeAnnotationNeeded,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'_> {
         let source_kind = "other";
         let source_name = "";
         let failure_span = None;
@@ -436,7 +436,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, error_code))]
     pub fn emit_inference_failure_err(
         &self,
@@ -445,7 +445,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         arg: GenericArg<'tcx>,
         error_code: TypeAnnotationNeeded,
         should_label_span: bool,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let arg = self.resolve_vars_if_possible(arg);
         let arg_data = self.extract_inference_diagnostics_data(arg, None);
 
@@ -453,7 +453,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // If we don't have any typeck results we're outside
             // of a body, so we won't be able to get better info
             // here.
-            return self.bad_inference_failure_err(failure_span, arg_data, error_code);
+            return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code);
         };
 
         let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg);
@@ -465,7 +465,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         let Some(InferSource { span, kind }) = local_visitor.infer_source else {
-            return self.bad_inference_failure_err(failure_span, arg_data, error_code);
+            return self.infcx.bad_inference_failure_err(failure_span, arg_data, error_code);
         };
 
         let (source_kind, name, path) = kind.ty_localized_msg(self);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 71a86683c21..9973646aecd 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -534,7 +534,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         let tcx = self.tcx();
 
         // Find the method being called.
-        let Ok(Some(instance)) = ty::Instance::resolve(
+        let Ok(Some(instance)) = ty::Instance::try_resolve(
             tcx,
             ctxt.param_env,
             ctxt.assoc_item.def_id,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
deleted file mode 100644
index 8fd19563c30..00000000000
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-use crate::errors::{
-    note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
-    RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
-};
-use crate::fluent_generated as fluent;
-use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
-use crate::infer::{self, SubregionOrigin};
-use rustc_errors::{Diag, Subdiagnostic};
-use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_middle::traits::ObligationCauseCode;
-use rustc_middle::ty::error::TypeError;
-use rustc_middle::ty::{self, IsSuggestable, Region, Ty};
-use rustc_span::symbol::kw;
-
-use super::ObligationCauseAsDiagArg;
-
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
-    pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
-        match *origin {
-            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
-                span: trace.cause.span,
-                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
-                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
-            }
-            .add_to_diag(err),
-            infer::Reborrow(span) => {
-                RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
-            }
-            infer::RelateObjectBound(span) => {
-                RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
-                    .add_to_diag(err);
-            }
-            infer::ReferenceOutlivesReferent(ty, span) => {
-                RegionOriginNote::WithName {
-                    span,
-                    msg: fluent::infer_reference_outlives_referent,
-                    name: &self.ty_to_string(ty),
-                    continues: false,
-                }
-                .add_to_diag(err);
-            }
-            infer::RelateParamBound(span, ty, opt_span) => {
-                RegionOriginNote::WithName {
-                    span,
-                    msg: fluent::infer_relate_param_bound,
-                    name: &self.ty_to_string(ty),
-                    continues: opt_span.is_some(),
-                }
-                .add_to_diag(err);
-                if let Some(span) = opt_span {
-                    RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
-                        .add_to_diag(err);
-                }
-            }
-            infer::RelateRegionParamBound(span) => {
-                RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
-                    .add_to_diag(err);
-            }
-            infer::CompareImplItemObligation { span, .. } => {
-                RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
-                    .add_to_diag(err);
-            }
-            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
-                self.note_region_origin(err, parent);
-            }
-            infer::AscribeUserTypeProvePredicate(span) => {
-                RegionOriginNote::Plain {
-                    span,
-                    msg: fluent::infer_ascribe_user_type_prove_predicate,
-                }
-                .add_to_diag(err);
-            }
-        }
-    }
-
-    pub(super) fn report_concrete_failure(
-        &self,
-        generic_param_scope: LocalDefId,
-        origin: SubregionOrigin<'tcx>,
-        sub: Region<'tcx>,
-        sup: Region<'tcx>,
-    ) -> Diag<'tcx> {
-        let mut err = match origin {
-            infer::Subtype(box trace) => {
-                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
-                let mut err = self.report_and_explain_type_error(trace, terr);
-                match (*sub, *sup) {
-                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
-                    (ty::RePlaceholder(_), _) => {
-                        note_and_explain_region(
-                            self.tcx,
-                            &mut err,
-                            generic_param_scope,
-                            "",
-                            sup,
-                            " doesn't meet the lifetime requirements",
-                            None,
-                        );
-                    }
-                    (_, ty::RePlaceholder(_)) => {
-                        note_and_explain_region(
-                            self.tcx,
-                            &mut err,
-                            generic_param_scope,
-                            "the required lifetime does not necessarily outlive ",
-                            sub,
-                            "",
-                            None,
-                        );
-                    }
-                    _ => {
-                        note_and_explain_region(
-                            self.tcx,
-                            &mut err,
-                            generic_param_scope,
-                            "",
-                            sup,
-                            "...",
-                            None,
-                        );
-                        note_and_explain_region(
-                            self.tcx,
-                            &mut err,
-                            generic_param_scope,
-                            "...does not necessarily outlive ",
-                            sub,
-                            "",
-                            None,
-                        );
-                    }
-                }
-                err
-            }
-            infer::Reborrow(span) => {
-                let reference_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    None,
-                    note_and_explain::PrefixKind::RefValidFor,
-                    note_and_explain::SuffixKind::Continues,
-                );
-                let content_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sup,
-                    None,
-                    note_and_explain::PrefixKind::ContentValidFor,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                self.dcx().create_err(OutlivesContent {
-                    span,
-                    notes: reference_valid.into_iter().chain(content_valid).collect(),
-                })
-            }
-            infer::RelateObjectBound(span) => {
-                let object_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    None,
-                    note_and_explain::PrefixKind::TypeObjValidFor,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                let pointer_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sup,
-                    None,
-                    note_and_explain::PrefixKind::SourcePointerValidFor,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                self.dcx().create_err(OutlivesBound {
-                    span,
-                    notes: object_valid.into_iter().chain(pointer_valid).collect(),
-                })
-            }
-            infer::RelateParamBound(span, ty, opt_span) => {
-                let prefix = match *sub {
-                    ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
-                    _ => note_and_explain::PrefixKind::TypeOutlive,
-                };
-                let suffix = if opt_span.is_some() {
-                    note_and_explain::SuffixKind::ReqByBinding
-                } else {
-                    note_and_explain::SuffixKind::Empty
-                };
-                let note = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    opt_span,
-                    prefix,
-                    suffix,
-                );
-                self.dcx().create_err(FulfillReqLifetime {
-                    span,
-                    ty: self.resolve_vars_if_possible(ty),
-                    note,
-                })
-            }
-            infer::RelateRegionParamBound(span) => {
-                let param_instantiated = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sup,
-                    None,
-                    note_and_explain::PrefixKind::LfParamInstantiatedWith,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                let param_must_outlive = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    None,
-                    note_and_explain::PrefixKind::LfParamMustOutlive,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                self.dcx().create_err(LfBoundNotSatisfied {
-                    span,
-                    notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
-                })
-            }
-            infer::ReferenceOutlivesReferent(ty, span) => {
-                let pointer_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    None,
-                    note_and_explain::PrefixKind::PointerValidFor,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                let data_valid = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sup,
-                    None,
-                    note_and_explain::PrefixKind::DataValidFor,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                self.dcx().create_err(RefLongerThanData {
-                    span,
-                    ty: self.resolve_vars_if_possible(ty),
-                    notes: pointer_valid.into_iter().chain(data_valid).collect(),
-                })
-            }
-            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
-                let mut err = self.report_extra_impl_obligation(
-                    span,
-                    impl_item_def_id,
-                    trait_item_def_id,
-                    &format!("`{sup}: {sub}`"),
-                );
-                // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
-                if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
-                    && generics.where_clause_span.contains(span)
-                {
-                    self.suggest_copy_trait_method_bounds(
-                        trait_item_def_id,
-                        impl_item_def_id,
-                        &mut err,
-                    );
-                }
-                err
-            }
-            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
-                let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
-
-                // Don't mention the item name if it's an RPITIT, since that'll just confuse
-                // folks.
-                if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
-                    let trait_item_span = self.tcx.def_span(trait_item_def_id);
-                    let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
-                    err.span_label(
-                        trait_item_span,
-                        format!("definition of `{item_name}` from trait"),
-                    );
-                }
-
-                self.suggest_copy_trait_method_bounds(
-                    trait_item_def_id,
-                    impl_item_def_id,
-                    &mut err,
-                );
-                err
-            }
-            infer::AscribeUserTypeProvePredicate(span) => {
-                let instantiated = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sup,
-                    None,
-                    note_and_explain::PrefixKind::LfInstantiatedWith,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                let must_outlive = note_and_explain::RegionExplanation::new(
-                    self.tcx,
-                    generic_param_scope,
-                    sub,
-                    None,
-                    note_and_explain::PrefixKind::LfMustOutlive,
-                    note_and_explain::SuffixKind::Empty,
-                );
-                self.dcx().create_err(LfBoundNotSatisfied {
-                    span,
-                    notes: instantiated.into_iter().chain(must_outlive).collect(),
-                })
-            }
-        };
-        if sub.is_error() || sup.is_error() {
-            err.downgrade_to_delayed_bug();
-        }
-        err
-    }
-
-    pub fn suggest_copy_trait_method_bounds(
-        &self,
-        trait_item_def_id: DefId,
-        impl_item_def_id: LocalDefId,
-        err: &mut Diag<'_>,
-    ) {
-        // FIXME(compiler-errors): Right now this is only being used for region
-        // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
-        // but right now it's not really very smart when it comes to implicit `Sized`
-        // predicates and bounds on the trait itself.
-
-        let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
-        else {
-            return;
-        };
-        let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
-            return;
-        };
-        let trait_args = trait_ref
-            .instantiate_identity()
-            // Replace the explicit self type with `Self` for better suggestion rendering
-            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
-            .args;
-        let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
-            .rebase_onto(self.tcx, impl_def_id, trait_args);
-
-        let Ok(trait_predicates) =
-            self.tcx
-                .explicit_predicates_of(trait_item_def_id)
-                .instantiate_own(self.tcx, trait_item_args)
-                .map(|(pred, _)| {
-                    if pred.is_suggestable(self.tcx, false) {
-                        Ok(pred.to_string())
-                    } else {
-                        Err(())
-                    }
-                })
-                .collect::<Result<Vec<_>, ()>>()
-        else {
-            return;
-        };
-
-        let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
-            return;
-        };
-
-        let suggestion = if trait_predicates.is_empty() {
-            WhereClauseSuggestions::Remove { span: generics.where_clause_span }
-        } else {
-            let space = if generics.where_clause_span.is_empty() { " " } else { "" };
-            WhereClauseSuggestions::CopyPredicates {
-                span: generics.where_clause_span,
-                space,
-                trait_predicates: trait_predicates.join(", "),
-            }
-        };
-        err.subdiagnostic(suggestion);
-    }
-
-    pub(super) fn report_placeholder_failure(
-        &self,
-        generic_param_scope: LocalDefId,
-        placeholder_origin: SubregionOrigin<'tcx>,
-        sub: Region<'tcx>,
-        sup: Region<'tcx>,
-    ) -> Diag<'tcx> {
-        // I can't think how to do better than this right now. -nikomatsakis
-        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
-        match placeholder_origin {
-            infer::Subtype(box ref trace)
-                if matches!(
-                    &trace.cause.code().peel_derives(),
-                    ObligationCauseCode::WhereClause(..)
-                        | ObligationCauseCode::WhereClauseInExpr(..)
-                ) =>
-            {
-                // Hack to get around the borrow checker because trace.cause has an `Rc`.
-                if let ObligationCauseCode::WhereClause(_, span)
-                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
-                    &trace.cause.code().peel_derives()
-                    && !span.is_dummy()
-                {
-                    let span = *span;
-                    self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
-                        .with_span_note(span, "the lifetime requirement is introduced here")
-                } else {
-                    unreachable!(
-                        "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
-                    )
-                }
-            }
-            infer::Subtype(box trace) => {
-                let terr = TypeError::RegionsPlaceholderMismatch;
-                return self.report_and_explain_type_error(trace, terr);
-            }
-            _ => {
-                return self.report_concrete_failure(
-                    generic_param_scope,
-                    placeholder_origin,
-                    sub,
-                    sup,
-                );
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index bc59b5e033b..d5e7de897d0 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -52,10 +52,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         ) = tcx.sess.source_map().span_to_snippet(sp) =>
                     {
                         if snippet.chars().all(|c| c.is_digit(10) || c == '-' || c == '_') {
-                            diag.span_suggestion(
-                                sp,
+                            diag.span_suggestion_verbose(
+                                sp.shrink_to_hi(),
                                 "use a float literal",
-                                format!("{snippet}.0"),
+                                ".0",
                                 MachineApplicable,
                             );
                         }
@@ -820,7 +820,7 @@ fn foo(&self) -> Self::T { String::new() }
                                 tcx.defaultness(item.id.owner_id)
                             {
                                 let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
-                                if self.infcx.can_eq(param_env, assoc_ty, found) {
+                                if self.infcx.can_eq_shallow(param_env, assoc_ty, found) {
                                     diag.span_label(
                                         item.span,
                                         "associated type defaults can't be assumed inside the \
@@ -843,7 +843,7 @@ fn foo(&self) -> Self::T { String::new() }
                         let assoc_ty = tcx.type_of(item.id.owner_id).instantiate_identity();
                         if let hir::Defaultness::Default { has_value: true } =
                             tcx.defaultness(item.id.owner_id)
-                            && self.infcx.can_eq(param_env, assoc_ty, found)
+                            && self.infcx.can_eq_shallow(param_env, assoc_ty, found)
                         {
                             diag.span_label(
                                 item.span,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/region.rs b/compiler/rustc_infer/src/infer/error_reporting/region.rs
new file mode 100644
index 00000000000..5a465f46e47
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/region.rs
@@ -0,0 +1,1259 @@
+use std::iter;
+
+use rustc_errors::{
+    struct_span_code_err, Applicability, Diag, Subdiagnostic, E0309, E0310, E0311, E0495,
+};
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, ParamName};
+use rustc_middle::bug;
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, IsSuggestable, Region, Ty, TyCtxt, TypeVisitableExt as _};
+use rustc_span::symbol::kw;
+use rustc_span::{ErrorGuaranteed, Span};
+use rustc_type_ir::Upcast as _;
+
+use super::nice_region_error::find_anon_type;
+use super::{nice_region_error, ObligationCauseAsDiagArg};
+use crate::errors::{
+    self, note_and_explain, FulfillReqLifetime, LfBoundNotSatisfied, OutlivesBound,
+    OutlivesContent, RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
+};
+use crate::fluent_generated as fluent;
+use crate::infer::error_reporting::{ObligationCauseExt as _, TypeErrCtxt};
+use crate::infer::region_constraints::GenericKind;
+use crate::infer::{self, InferCtxt, RegionResolutionError, RegionVariableOrigin, SubregionOrigin};
+
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    pub fn report_region_errors(
+        &self,
+        generic_param_scope: LocalDefId,
+        errors: &[RegionResolutionError<'tcx>],
+    ) -> ErrorGuaranteed {
+        assert!(!errors.is_empty());
+
+        if let Some(guaranteed) = self.infcx.tainted_by_errors() {
+            return guaranteed;
+        }
+
+        debug!("report_region_errors(): {} errors to start", errors.len());
+
+        // try to pre-process the errors, which will group some of them
+        // together into a `ProcessedErrors` group:
+        let errors = self.process_errors(errors);
+
+        debug!("report_region_errors: {} errors after preprocessing", errors.len());
+
+        let mut guar = None;
+        for error in errors {
+            debug!("report_region_errors: error = {:?}", error);
+
+            let e = if let Some(guar) =
+                self.try_report_nice_region_error(generic_param_scope, &error)
+            {
+                guar
+            } else {
+                match error.clone() {
+                    // These errors could indicate all manner of different
+                    // problems with many different solutions. Rather
+                    // than generate a "one size fits all" error, what we
+                    // attempt to do is go through a number of specific
+                    // scenarios and try to find the best way to present
+                    // the error. If all of these fails, we fall back to a rather
+                    // general bit of code that displays the error information
+                    RegionResolutionError::ConcreteFailure(origin, sub, sup) => {
+                        if sub.is_placeholder() || sup.is_placeholder() {
+                            self.report_placeholder_failure(generic_param_scope, origin, sub, sup)
+                                .emit()
+                        } else {
+                            self.report_concrete_failure(generic_param_scope, origin, sub, sup)
+                                .emit()
+                        }
+                    }
+
+                    RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => self
+                        .report_generic_bound_failure(
+                            generic_param_scope,
+                            origin.span(),
+                            Some(origin),
+                            param_ty,
+                            sub,
+                        ),
+
+                    RegionResolutionError::SubSupConflict(
+                        _,
+                        var_origin,
+                        sub_origin,
+                        sub_r,
+                        sup_origin,
+                        sup_r,
+                        _,
+                    ) => {
+                        if sub_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                generic_param_scope,
+                                sub_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                            .emit()
+                        } else if sup_r.is_placeholder() {
+                            self.report_placeholder_failure(
+                                generic_param_scope,
+                                sup_origin,
+                                sub_r,
+                                sup_r,
+                            )
+                            .emit()
+                        } else {
+                            self.report_sub_sup_conflict(
+                                generic_param_scope,
+                                var_origin,
+                                sub_origin,
+                                sub_r,
+                                sup_origin,
+                                sup_r,
+                            )
+                        }
+                    }
+
+                    RegionResolutionError::UpperBoundUniverseConflict(
+                        _,
+                        _,
+                        _,
+                        sup_origin,
+                        sup_r,
+                    ) => {
+                        assert!(sup_r.is_placeholder());
+
+                        // Make a dummy value for the "sub region" --
+                        // this is the initial value of the
+                        // placeholder. In practice, we expect more
+                        // tailored errors that don't really use this
+                        // value.
+                        let sub_r = self.tcx.lifetimes.re_erased;
+
+                        self.report_placeholder_failure(
+                            generic_param_scope,
+                            sup_origin,
+                            sub_r,
+                            sup_r,
+                        )
+                        .emit()
+                    }
+
+                    RegionResolutionError::CannotNormalize(clause, origin) => {
+                        let clause: ty::Clause<'tcx> =
+                            clause.map_bound(ty::ClauseKind::TypeOutlives).upcast(self.tcx);
+                        self.tcx
+                            .dcx()
+                            .struct_span_err(origin.span(), format!("cannot normalize `{clause}`"))
+                            .emit()
+                    }
+                }
+            };
+
+            guar = Some(e)
+        }
+
+        guar.unwrap()
+    }
+
+    // This method goes through all the errors and try to group certain types
+    // of error together, for the purpose of suggesting explicit lifetime
+    // parameters to the user. This is done so that we can have a more
+    // complete view of what lifetimes should be the same.
+    // If the return value is an empty vector, it means that processing
+    // failed (so the return value of this method should not be used).
+    //
+    // The method also attempts to weed out messages that seem like
+    // duplicates that will be unhelpful to the end-user. But
+    // obviously it never weeds out ALL errors.
+    fn process_errors(
+        &self,
+        errors: &[RegionResolutionError<'tcx>],
+    ) -> Vec<RegionResolutionError<'tcx>> {
+        debug!("process_errors()");
+
+        // We want to avoid reporting generic-bound failures if we can
+        // avoid it: these have a very high rate of being unhelpful in
+        // practice. This is because they are basically secondary
+        // checks that test the state of the region graph after the
+        // rest of inference is done, and the other kinds of errors
+        // indicate that the region constraint graph is internally
+        // inconsistent, so these test results are likely to be
+        // meaningless.
+        //
+        // Therefore, we filter them out of the list unless they are
+        // the only thing in the list.
+
+        let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
+            RegionResolutionError::GenericBoundFailure(..) => true,
+            RegionResolutionError::ConcreteFailure(..)
+            | RegionResolutionError::SubSupConflict(..)
+            | RegionResolutionError::UpperBoundUniverseConflict(..)
+            | RegionResolutionError::CannotNormalize(..) => false,
+        };
+
+        let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
+            errors.to_owned()
+        } else {
+            errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect()
+        };
+
+        // sort the errors by span, for better error message stability.
+        errors.sort_by_key(|u| match *u {
+            RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
+            RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(),
+            RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(),
+            RegionResolutionError::CannotNormalize(_, ref sro) => sro.span(),
+        });
+        errors
+    }
+
+    pub(super) fn note_region_origin(&self, err: &mut Diag<'_>, origin: &SubregionOrigin<'tcx>) {
+        match *origin {
+            infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+                span: trace.cause.span,
+                requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+                expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)),
+            }
+            .add_to_diag(err),
+            infer::Reborrow(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diag(err)
+            }
+            infer::RelateObjectBound(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
+                    .add_to_diag(err);
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer_reference_outlives_referent,
+                    name: &self.ty_to_string(ty),
+                    continues: false,
+                }
+                .add_to_diag(err);
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                RegionOriginNote::WithName {
+                    span,
+                    msg: fluent::infer_relate_param_bound,
+                    name: &self.ty_to_string(ty),
+                    continues: opt_span.is_some(),
+                }
+                .add_to_diag(err);
+                if let Some(span) = opt_span {
+                    RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
+                        .add_to_diag(err);
+                }
+            }
+            infer::RelateRegionParamBound(span) => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
+                    .add_to_diag(err);
+            }
+            infer::CompareImplItemObligation { span, .. } => {
+                RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
+                    .add_to_diag(err);
+            }
+            infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+                self.note_region_origin(err, parent);
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                RegionOriginNote::Plain {
+                    span,
+                    msg: fluent::infer_ascribe_user_type_prove_predicate,
+                }
+                .add_to_diag(err);
+            }
+        }
+    }
+
+    pub(super) fn report_concrete_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        let mut err = match origin {
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+                let mut err = self.report_and_explain_type_error(trace, terr);
+                match (*sub, *sup) {
+                    (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+                    (ty::RePlaceholder(_), _) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            " doesn't meet the lifetime requirements",
+                            None,
+                        );
+                    }
+                    (_, ty::RePlaceholder(_)) => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "the required lifetime does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                    _ => {
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "",
+                            sup,
+                            "...",
+                            None,
+                        );
+                        note_and_explain_region(
+                            self.tcx,
+                            &mut err,
+                            generic_param_scope,
+                            "...does not necessarily outlive ",
+                            sub,
+                            "",
+                            None,
+                        );
+                    }
+                }
+                err
+            }
+            infer::Reborrow(span) => {
+                let reference_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::RefValidFor,
+                    note_and_explain::SuffixKind::Continues,
+                );
+                let content_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::ContentValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesContent {
+                    span,
+                    notes: reference_valid.into_iter().chain(content_valid).collect(),
+                })
+            }
+            infer::RelateObjectBound(span) => {
+                let object_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::TypeObjValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::SourcePointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(OutlivesBound {
+                    span,
+                    notes: object_valid.into_iter().chain(pointer_valid).collect(),
+                })
+            }
+            infer::RelateParamBound(span, ty, opt_span) => {
+                let prefix = match *sub {
+                    ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
+                    _ => note_and_explain::PrefixKind::TypeOutlive,
+                };
+                let suffix = if opt_span.is_some() {
+                    note_and_explain::SuffixKind::ReqByBinding
+                } else {
+                    note_and_explain::SuffixKind::Empty
+                };
+                let note = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    opt_span,
+                    prefix,
+                    suffix,
+                );
+                self.dcx().create_err(FulfillReqLifetime {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    note,
+                })
+            }
+            infer::RelateRegionParamBound(span) => {
+                let param_instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfParamInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let param_must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::LfParamMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
+                })
+            }
+            infer::ReferenceOutlivesReferent(ty, span) => {
+                let pointer_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::PointerValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let data_valid = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::DataValidFor,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(RefLongerThanData {
+                    span,
+                    ty: self.resolve_vars_if_possible(ty),
+                    notes: pointer_valid.into_iter().chain(data_valid).collect(),
+                })
+            }
+            infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
+                let mut err = self.infcx.report_extra_impl_obligation(
+                    span,
+                    impl_item_def_id,
+                    trait_item_def_id,
+                    &format!("`{sup}: {sub}`"),
+                );
+                // We should only suggest rewriting the `where` clause if the predicate is within that `where` clause
+                if let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id)
+                    && generics.where_clause_span.contains(span)
+                {
+                    self.suggest_copy_trait_method_bounds(
+                        trait_item_def_id,
+                        impl_item_def_id,
+                        &mut err,
+                    );
+                }
+                err
+            }
+            infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+                let mut err = self.report_concrete_failure(generic_param_scope, *parent, sub, sup);
+
+                // Don't mention the item name if it's an RPITIT, since that'll just confuse
+                // folks.
+                if !self.tcx.is_impl_trait_in_trait(impl_item_def_id.to_def_id()) {
+                    let trait_item_span = self.tcx.def_span(trait_item_def_id);
+                    let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+                    err.span_label(
+                        trait_item_span,
+                        format!("definition of `{item_name}` from trait"),
+                    );
+                }
+
+                self.suggest_copy_trait_method_bounds(
+                    trait_item_def_id,
+                    impl_item_def_id,
+                    &mut err,
+                );
+                err
+            }
+            infer::AscribeUserTypeProvePredicate(span) => {
+                let instantiated = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sup,
+                    None,
+                    note_and_explain::PrefixKind::LfInstantiatedWith,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                let must_outlive = note_and_explain::RegionExplanation::new(
+                    self.tcx,
+                    generic_param_scope,
+                    sub,
+                    None,
+                    note_and_explain::PrefixKind::LfMustOutlive,
+                    note_and_explain::SuffixKind::Empty,
+                );
+                self.dcx().create_err(LfBoundNotSatisfied {
+                    span,
+                    notes: instantiated.into_iter().chain(must_outlive).collect(),
+                })
+            }
+        };
+        if sub.is_error() || sup.is_error() {
+            err.downgrade_to_delayed_bug();
+        }
+        err
+    }
+
+    pub fn suggest_copy_trait_method_bounds(
+        &self,
+        trait_item_def_id: DefId,
+        impl_item_def_id: LocalDefId,
+        err: &mut Diag<'_>,
+    ) {
+        // FIXME(compiler-errors): Right now this is only being used for region
+        // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
+        // but right now it's not really very smart when it comes to implicit `Sized`
+        // predicates and bounds on the trait itself.
+
+        let Some(impl_def_id) = self.tcx.associated_item(impl_item_def_id).impl_container(self.tcx)
+        else {
+            return;
+        };
+        let Some(trait_ref) = self.tcx.impl_trait_ref(impl_def_id) else {
+            return;
+        };
+        let trait_args = trait_ref
+            .instantiate_identity()
+            // Replace the explicit self type with `Self` for better suggestion rendering
+            .with_self_ty(self.tcx, Ty::new_param(self.tcx, 0, kw::SelfUpper))
+            .args;
+        let trait_item_args = ty::GenericArgs::identity_for_item(self.tcx, impl_item_def_id)
+            .rebase_onto(self.tcx, impl_def_id, trait_args);
+
+        let Ok(trait_predicates) =
+            self.tcx
+                .explicit_predicates_of(trait_item_def_id)
+                .instantiate_own(self.tcx, trait_item_args)
+                .map(|(pred, _)| {
+                    if pred.is_suggestable(self.tcx, false) {
+                        Ok(pred.to_string())
+                    } else {
+                        Err(())
+                    }
+                })
+                .collect::<Result<Vec<_>, ()>>()
+        else {
+            return;
+        };
+
+        let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else {
+            return;
+        };
+
+        let suggestion = if trait_predicates.is_empty() {
+            WhereClauseSuggestions::Remove { span: generics.where_clause_span }
+        } else {
+            let space = if generics.where_clause_span.is_empty() { " " } else { "" };
+            WhereClauseSuggestions::CopyPredicates {
+                span: generics.where_clause_span,
+                space,
+                trait_predicates: trait_predicates.join(", "),
+            }
+        };
+        err.subdiagnostic(suggestion);
+    }
+
+    pub(super) fn report_placeholder_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        placeholder_origin: SubregionOrigin<'tcx>,
+        sub: Region<'tcx>,
+        sup: Region<'tcx>,
+    ) -> Diag<'a> {
+        // I can't think how to do better than this right now. -nikomatsakis
+        debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+        match placeholder_origin {
+            infer::Subtype(box ref trace)
+                if matches!(
+                    &trace.cause.code().peel_derives(),
+                    ObligationCauseCode::WhereClause(..)
+                        | ObligationCauseCode::WhereClauseInExpr(..)
+                ) =>
+            {
+                // Hack to get around the borrow checker because trace.cause has an `Rc`.
+                if let ObligationCauseCode::WhereClause(_, span)
+                | ObligationCauseCode::WhereClauseInExpr(_, span, ..) =
+                    &trace.cause.code().peel_derives()
+                    && !span.is_dummy()
+                {
+                    let span = *span;
+                    self.report_concrete_failure(generic_param_scope, placeholder_origin, sub, sup)
+                        .with_span_note(span, "the lifetime requirement is introduced here")
+                } else {
+                    unreachable!(
+                        "control flow ensures we have a `BindingObligation` or `WhereClauseInExpr` here..."
+                    )
+                }
+            }
+            infer::Subtype(box trace) => {
+                let terr = TypeError::RegionsPlaceholderMismatch;
+                return self.report_and_explain_type_error(trace, terr);
+            }
+            _ => {
+                return self.report_concrete_failure(
+                    generic_param_scope,
+                    placeholder_origin,
+                    sub,
+                    sup,
+                );
+            }
+        }
+    }
+
+    pub fn report_generic_bound_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) -> ErrorGuaranteed {
+        self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub)
+            .emit()
+    }
+
+    pub fn construct_generic_bound_failure(
+        &self,
+        generic_param_scope: LocalDefId,
+        span: Span,
+        origin: Option<SubregionOrigin<'tcx>>,
+        bound_kind: GenericKind<'tcx>,
+        sub: Region<'tcx>,
+    ) -> Diag<'a> {
+        if let Some(SubregionOrigin::CompareImplItemObligation {
+            span,
+            impl_item_def_id,
+            trait_item_def_id,
+        }) = origin
+        {
+            return self.infcx.report_extra_impl_obligation(
+                span,
+                impl_item_def_id,
+                trait_item_def_id,
+                &format!("`{bound_kind}: {sub}`"),
+            );
+        }
+
+        let labeled_user_string = match bound_kind {
+            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::Projection | ty::Inherent => {
+                    format!("the associated 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(span, format!("{labeled_user_string} may not live long enough"));
+        err.code(match sub.kind() {
+            ty::ReEarlyParam(_) | ty::ReLateParam(_) if sub.has_name() => E0309,
+            ty::ReStatic => E0310,
+            _ => E0311,
+        });
+
+        '_explain: {
+            let (description, span) = match sub.kind() {
+                ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+                    msg_span_from_named_region(self.tcx, generic_param_scope, sub, Some(span))
+                }
+                _ => (format!("lifetime `{sub}`"), Some(span)),
+            };
+            let prefix = format!("{labeled_user_string} must be valid for ");
+            label_msg_span(&mut err, &prefix, description, span, "...");
+            if let Some(origin) = origin {
+                self.note_region_origin(&mut err, &origin);
+            }
+        }
+
+        'suggestion: {
+            let msg = "consider adding an explicit lifetime bound";
+
+            if (bound_kind, sub).has_infer_regions()
+                || (bound_kind, sub).has_placeholders()
+                || !bound_kind.is_suggestable(self.tcx, false)
+            {
+                let lt_name = sub.get_name_or_anon().to_string();
+                err.help(format!("{msg} `{bound_kind}: {lt_name}`..."));
+                break 'suggestion;
+            }
+
+            let mut generic_param_scope = generic_param_scope;
+            while self.tcx.def_kind(generic_param_scope) == DefKind::OpaqueTy {
+                generic_param_scope = self.tcx.local_parent(generic_param_scope);
+            }
+
+            // type_param_sugg_span is (span, has_bounds, needs_parentheses)
+            let (type_scope, type_param_sugg_span) = match bound_kind {
+                GenericKind::Param(param) => {
+                    let generics = self.tcx.generics_of(generic_param_scope);
+                    let type_param = generics.type_param(param, self.tcx);
+                    let def_id = type_param.def_id.expect_local();
+                    let scope = self.tcx.local_def_id_to_hir_id(def_id).owner.def_id;
+                    // Get the `hir::Param` to verify whether it already has any bounds.
+                    // We do this to avoid suggesting code that ends up as `T: 'a'b`,
+                    // instead we suggest `T: 'a + 'b` in that case.
+                    let hir_generics = self.tcx.hir().get_generics(scope).unwrap();
+                    let sugg_span = match hir_generics.bounds_span_for_suggestions(def_id) {
+                        Some((span, open_paren_sp)) => Some((span, true, open_paren_sp)),
+                        // If `param` corresponds to `Self`, no usable suggestion span.
+                        None if generics.has_self && param.index == 0 => None,
+                        None => {
+                            let span = if let Some(param) =
+                                hir_generics.params.iter().find(|param| param.def_id == def_id)
+                                && let ParamName::Plain(ident) = param.name
+                            {
+                                ident.span.shrink_to_hi()
+                            } else {
+                                let span = self.tcx.def_span(def_id);
+                                span.shrink_to_hi()
+                            };
+                            Some((span, false, None))
+                        }
+                    };
+                    (scope, sugg_span)
+                }
+                _ => (generic_param_scope, None),
+            };
+            let suggestion_scope = {
+                let lifetime_scope = match sub.kind() {
+                    ty::ReStatic => hir::def_id::CRATE_DEF_ID,
+                    _ => match self.tcx.is_suitable_region(generic_param_scope, sub) {
+                        Some(info) => info.def_id,
+                        None => generic_param_scope,
+                    },
+                };
+                match self.tcx.is_descendant_of(type_scope.into(), lifetime_scope.into()) {
+                    true => type_scope,
+                    false => lifetime_scope,
+                }
+            };
+
+            let mut suggs = vec![];
+            let lt_name = self.suggest_name_region(generic_param_scope, sub, &mut suggs);
+
+            if let Some((sp, has_lifetimes, open_paren_sp)) = type_param_sugg_span
+                && suggestion_scope == type_scope
+            {
+                let suggestion =
+                    if has_lifetimes { format!(" + {lt_name}") } else { format!(": {lt_name}") };
+
+                if let Some(open_paren_sp) = open_paren_sp {
+                    suggs.push((open_paren_sp, "(".to_string()));
+                    suggs.push((sp, format!("){suggestion}")));
+                } else {
+                    suggs.push((sp, suggestion))
+                }
+            } else if let GenericKind::Alias(ref p) = bound_kind
+                && let ty::Projection = p.kind(self.tcx)
+                && let DefKind::AssocTy = self.tcx.def_kind(p.def_id)
+                && let Some(ty::ImplTraitInTraitData::Trait { .. }) =
+                    self.tcx.opt_rpitit_info(p.def_id)
+            {
+                // The lifetime found in the `impl` is longer than the one on the RPITIT.
+                // Do not suggest `<Type as Trait>::{opaque}: 'static`.
+            } else if let Some(generics) = self.tcx.hir().get_generics(suggestion_scope) {
+                let pred = format!("{bound_kind}: {lt_name}");
+                let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred);
+                suggs.push((generics.tail_span_for_predicate_suggestion(), suggestion))
+            } else {
+                let consider = format!("{msg} `{bound_kind}: {sub}`...");
+                err.help(consider);
+            }
+
+            if !suggs.is_empty() {
+                err.multipart_suggestion_verbose(
+                    msg,
+                    suggs,
+                    Applicability::MaybeIncorrect, // Issue #41966
+                );
+            }
+        }
+
+        err
+    }
+
+    pub fn suggest_name_region(
+        &self,
+        generic_param_scope: LocalDefId,
+        lifetime: Region<'tcx>,
+        add_lt_suggs: &mut Vec<(Span, String)>,
+    ) -> String {
+        struct LifetimeReplaceVisitor<'tcx, 'a> {
+            tcx: TyCtxt<'tcx>,
+            needle: hir::LifetimeName,
+            new_lt: &'a str,
+            add_lt_suggs: &'a mut Vec<(Span, String)>,
+        }
+
+        impl<'hir, 'tcx> hir::intravisit::Visitor<'hir> for LifetimeReplaceVisitor<'tcx, '_> {
+            fn visit_lifetime(&mut self, lt: &'hir hir::Lifetime) {
+                if lt.res == self.needle {
+                    let (pos, span) = lt.suggestion_position();
+                    let new_lt = &self.new_lt;
+                    let sugg = match pos {
+                        hir::LifetimeSuggestionPosition::Normal => format!("{new_lt}"),
+                        hir::LifetimeSuggestionPosition::Ampersand => format!("{new_lt} "),
+                        hir::LifetimeSuggestionPosition::ElidedPath => format!("<{new_lt}>"),
+                        hir::LifetimeSuggestionPosition::ElidedPathArgument => {
+                            format!("{new_lt}, ")
+                        }
+                        hir::LifetimeSuggestionPosition::ObjectDefault => format!("+ {new_lt}"),
+                    };
+                    self.add_lt_suggs.push((span, sugg));
+                }
+            }
+
+            fn visit_ty(&mut self, ty: &'hir hir::Ty<'hir>) {
+                let hir::TyKind::OpaqueDef(item_id, _, _) = ty.kind else {
+                    return hir::intravisit::walk_ty(self, ty);
+                };
+                let opaque_ty = self.tcx.hir().item(item_id).expect_opaque_ty();
+                if let Some(&(_, b)) =
+                    opaque_ty.lifetime_mapping.iter().find(|&(a, _)| a.res == self.needle)
+                {
+                    let prev_needle =
+                        std::mem::replace(&mut self.needle, hir::LifetimeName::Param(b));
+                    for bound in opaque_ty.bounds {
+                        self.visit_param_bound(bound);
+                    }
+                    self.needle = prev_needle;
+                }
+            }
+        }
+
+        let (lifetime_def_id, lifetime_scope) =
+            match self.tcx.is_suitable_region(generic_param_scope, lifetime) {
+                Some(info) if !lifetime.has_name() => {
+                    (info.bound_region.get_id().unwrap().expect_local(), info.def_id)
+                }
+                _ => return lifetime.get_name_or_anon().to_string(),
+            };
+
+        let new_lt = {
+            let generics = self.tcx.generics_of(lifetime_scope);
+            let mut used_names =
+                iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p)))
+                    .flat_map(|g| &g.own_params)
+                    .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime))
+                    .map(|p| p.name)
+                    .collect::<Vec<_>>();
+            let hir_id = self.tcx.local_def_id_to_hir_id(lifetime_scope);
+            // consider late-bound lifetimes ...
+            used_names.extend(self.tcx.late_bound_vars(hir_id).into_iter().filter_map(
+                |p| match p {
+                    ty::BoundVariableKind::Region(lt) => lt.get_name(),
+                    _ => None,
+                },
+            ));
+            (b'a'..=b'z')
+                .map(|c| format!("'{}", c as char))
+                .find(|candidate| !used_names.iter().any(|e| e.as_str() == candidate))
+                .unwrap_or("'lt".to_string())
+        };
+
+        let mut visitor = LifetimeReplaceVisitor {
+            tcx: self.tcx,
+            needle: hir::LifetimeName::Param(lifetime_def_id),
+            add_lt_suggs,
+            new_lt: &new_lt,
+        };
+        match self.tcx.expect_hir_owner_node(lifetime_scope) {
+            hir::OwnerNode::Item(i) => visitor.visit_item(i),
+            hir::OwnerNode::ForeignItem(i) => visitor.visit_foreign_item(i),
+            hir::OwnerNode::ImplItem(i) => visitor.visit_impl_item(i),
+            hir::OwnerNode::TraitItem(i) => visitor.visit_trait_item(i),
+            hir::OwnerNode::Crate(_) => bug!("OwnerNode::Crate doesn't not have generics"),
+            hir::OwnerNode::Synthetic => unreachable!(),
+        }
+
+        let ast_generics = self.tcx.hir().get_generics(lifetime_scope).unwrap();
+        let sugg = ast_generics
+            .span_for_lifetime_suggestion()
+            .map(|span| (span, format!("{new_lt}, ")))
+            .unwrap_or_else(|| (ast_generics.span, format!("<{new_lt}>")));
+        add_lt_suggs.push(sugg);
+
+        new_lt
+    }
+
+    fn report_sub_sup_conflict(
+        &self,
+        generic_param_scope: LocalDefId,
+        var_origin: RegionVariableOrigin,
+        sub_origin: SubregionOrigin<'tcx>,
+        sub_region: Region<'tcx>,
+        sup_origin: SubregionOrigin<'tcx>,
+        sup_region: Region<'tcx>,
+    ) -> ErrorGuaranteed {
+        let mut err = self.report_inference_failure(var_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            generic_param_scope,
+            "first, the lifetime cannot outlive ",
+            sup_region,
+            "...",
+            None,
+        );
+
+        debug!("report_sub_sup_conflict: var_origin={:?}", var_origin);
+        debug!("report_sub_sup_conflict: sub_region={:?}", sub_region);
+        debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin);
+        debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
+        debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
+
+        if let infer::Subtype(ref sup_trace) = sup_origin
+            && let infer::Subtype(ref sub_trace) = sub_origin
+            && let Some((sup_expected, sup_found, _)) = self.values_str(sup_trace.values)
+            && let Some((sub_expected, sub_found, _)) = self.values_str(sub_trace.values)
+            && sub_expected == sup_expected
+            && sub_found == sup_found
+        {
+            note_and_explain_region(
+                self.tcx,
+                &mut err,
+                generic_param_scope,
+                "...but the lifetime must also be valid for ",
+                sub_region,
+                "...",
+                None,
+            );
+            err.span_note(
+                sup_trace.cause.span,
+                format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+            );
+
+            err.note_expected_found(&"", sup_expected, &"", sup_found);
+            return if sub_region.is_error() | sup_region.is_error() {
+                err.delay_as_bug()
+            } else {
+                err.emit()
+            };
+        }
+
+        self.note_region_origin(&mut err, &sup_origin);
+
+        note_and_explain_region(
+            self.tcx,
+            &mut err,
+            generic_param_scope,
+            "but, the lifetime must be valid for ",
+            sub_region,
+            "...",
+            None,
+        );
+
+        self.note_region_origin(&mut err, &sub_origin);
+        if sub_region.is_error() | sup_region.is_error() { err.delay_as_bug() } else { err.emit() }
+    }
+
+    fn report_inference_failure(&self, var_origin: RegionVariableOrigin) -> Diag<'_> {
+        let br_string = |br: ty::BoundRegionKind| {
+            let mut s = match br {
+                ty::BrNamed(_, name) => name.to_string(),
+                _ => String::new(),
+            };
+            if !s.is_empty() {
+                s.push(' ');
+            }
+            s
+        };
+        let var_description = match var_origin {
+            infer::MiscVariable(_) => String::new(),
+            infer::PatternRegion(_) => " for pattern".to_string(),
+            infer::AddrOfRegion(_) => " for borrow expression".to_string(),
+            infer::Autoref(_) => " for autoref".to_string(),
+            infer::Coercion(_) => " for automatic coercion".to_string(),
+            infer::BoundRegion(_, br, infer::FnCall) => {
+                format!(" for lifetime parameter {}in function call", br_string(br))
+            }
+            infer::BoundRegion(_, br, infer::HigherRankedType) => {
+                format!(" for lifetime parameter {}in generic type", br_string(br))
+            }
+            infer::BoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!(
+                " for lifetime parameter {}in trait containing associated type `{}`",
+                br_string(br),
+                self.tcx.associated_item(def_id).name
+            ),
+            infer::RegionParameterDefinition(_, name) => {
+                format!(" for lifetime parameter `{name}`")
+            }
+            infer::UpvarRegion(ref upvar_id, _) => {
+                let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+                format!(" for capture of `{var_name}` by closure")
+            }
+            infer::Nll(..) => bug!("NLL variable found in lexical phase"),
+        };
+
+        struct_span_code_err!(
+            self.dcx(),
+            var_origin.span(),
+            E0495,
+            "cannot infer an appropriate lifetime{} due to conflicting requirements",
+            var_description
+        )
+    }
+}
+
+pub(super) fn note_and_explain_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_>,
+    generic_param_scope: LocalDefId,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+    alt_span: Option<Span>,
+) {
+    let (description, span) = match *region {
+        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::RePlaceholder(_) | ty::ReStatic => {
+            msg_span_from_named_region(tcx, generic_param_scope, region, alt_span)
+        }
+
+        ty::ReError(_) => return,
+
+        // FIXME(#125431): `ReVar` shouldn't reach here.
+        ty::ReVar(_) => (format!("lifetime `{region}`"), alt_span),
+
+        ty::ReBound(..) | ty::ReErased => {
+            bug!("unexpected region for note_and_explain_region: {:?}", region);
+        }
+    };
+
+    emit_msg_span(err, prefix, description, span, suffix);
+}
+
+fn explain_free_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_>,
+    generic_param_scope: LocalDefId,
+    prefix: &str,
+    region: ty::Region<'tcx>,
+    suffix: &str,
+) {
+    let (description, span) = msg_span_from_named_region(tcx, generic_param_scope, region, None);
+
+    label_msg_span(err, prefix, description, span, suffix);
+}
+
+fn msg_span_from_named_region<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    region: ty::Region<'tcx>,
+    alt_span: Option<Span>,
+) -> (String, Option<Span>) {
+    match *region {
+        ty::ReEarlyParam(br) => {
+            let scope = tcx
+                .parent(tcx.generics_of(generic_param_scope).region_param(br, tcx).def_id)
+                .expect_local();
+            let span = if let Some(param) =
+                tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name))
+            {
+                param.span
+            } else {
+                tcx.def_span(scope)
+            };
+            let text = if br.has_name() {
+                format!("the lifetime `{}` as defined here", br.name)
+            } else {
+                "the anonymous lifetime as defined here".to_string()
+            };
+            (text, Some(span))
+        }
+        ty::ReLateParam(ref fr) => {
+            if !fr.bound_region.is_named()
+                && let Some((ty, _)) =
+                    find_anon_type(tcx, generic_param_scope, region, &fr.bound_region)
+            {
+                ("the anonymous lifetime defined here".to_string(), Some(ty.span))
+            } else {
+                match fr.bound_region {
+                    ty::BoundRegionKind::BrNamed(_, name) => {
+                        let span = if let Some(param) = tcx
+                            .hir()
+                            .get_generics(generic_param_scope)
+                            .and_then(|generics| generics.get_named(name))
+                        {
+                            param.span
+                        } else {
+                            tcx.def_span(generic_param_scope)
+                        };
+                        let text = if name == kw::UnderscoreLifetime {
+                            "the anonymous lifetime as defined here".to_string()
+                        } else {
+                            format!("the lifetime `{name}` as defined here")
+                        };
+                        (text, Some(span))
+                    }
+                    ty::BrAnon => (
+                        "the anonymous lifetime as defined here".to_string(),
+                        Some(tcx.def_span(generic_param_scope)),
+                    ),
+                    _ => (
+                        format!("the lifetime `{region}` as defined here"),
+                        Some(tcx.def_span(generic_param_scope)),
+                    ),
+                }
+            }
+        }
+        ty::ReStatic => ("the static lifetime".to_owned(), alt_span),
+        ty::RePlaceholder(ty::PlaceholderRegion {
+            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrNamed(def_id, name), .. },
+            ..
+        }) => (format!("the lifetime `{name}` as defined here"), Some(tcx.def_span(def_id))),
+        ty::RePlaceholder(ty::PlaceholderRegion {
+            bound: ty::BoundRegion { kind: ty::BoundRegionKind::BrAnon, .. },
+            ..
+        }) => ("an anonymous lifetime".to_owned(), None),
+        _ => bug!("{:?}", region),
+    }
+}
+
+fn emit_msg_span(
+    err: &mut Diag<'_>,
+    prefix: &str,
+    description: String,
+    span: Option<Span>,
+    suffix: &str,
+) {
+    let message = format!("{prefix}{description}{suffix}");
+
+    if let Some(span) = span {
+        err.span_note(span, message);
+    } else {
+        err.note(message);
+    }
+}
+
+fn label_msg_span(
+    err: &mut Diag<'_>,
+    prefix: &str,
+    description: String,
+    span: Option<Span>,
+    suffix: &str,
+) {
+    let message = format!("{prefix}{description}{suffix}");
+
+    if let Some(span) = span {
+        err.span_label(span, message);
+    } else {
+        err.note(message);
+    }
+}
+
+#[instrument(level = "trace", skip(infcx))]
+pub fn unexpected_hidden_region_diagnostic<'a, 'tcx>(
+    infcx: &'a InferCtxt<'tcx>,
+    generic_param_scope: LocalDefId,
+    span: Span,
+    hidden_ty: Ty<'tcx>,
+    hidden_region: ty::Region<'tcx>,
+    opaque_ty_key: ty::OpaqueTypeKey<'tcx>,
+) -> Diag<'a> {
+    let tcx = infcx.tcx;
+    let mut err = infcx.dcx().create_err(errors::OpaqueCapturesLifetime {
+        span,
+        opaque_ty: Ty::new_opaque(tcx, opaque_ty_key.def_id.to_def_id(), opaque_ty_key.args),
+        opaque_ty_span: tcx.def_span(opaque_ty_key.def_id),
+    });
+
+    // Explain the region we are capturing.
+    match *hidden_region {
+        ty::ReEarlyParam(_) | ty::ReLateParam(_) | ty::ReStatic => {
+            // Assuming regionck succeeded (*), we ought to always be
+            // capturing *some* region from the fn header, and hence it
+            // ought to be free. So under normal circumstances, we will go
+            // down this path which gives a decent human readable
+            // explanation.
+            //
+            // (*) if not, the `tainted_by_errors` field would be set to
+            // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all.
+            explain_free_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{hidden_ty}` captures "),
+                hidden_region,
+                "",
+            );
+            if let Some(reg_info) = tcx.is_suitable_region(generic_param_scope, hidden_region) {
+                let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id);
+                nice_region_error::suggest_new_region_bound(
+                    tcx,
+                    &mut err,
+                    fn_returns,
+                    hidden_region.to_string(),
+                    None,
+                    format!("captures `{hidden_region}`"),
+                    None,
+                    Some(reg_info.def_id),
+                )
+            }
+        }
+        ty::RePlaceholder(_) => {
+            explain_free_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{}` captures ", hidden_ty),
+                hidden_region,
+                "",
+            );
+        }
+        ty::ReError(_) => {
+            err.downgrade_to_delayed_bug();
+        }
+        _ => {
+            // Ugh. This is a painful case: the hidden region is not one
+            // that we can easily summarize or explain. This can happen
+            // in a case like
+            // `tests/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`:
+            //
+            // ```
+            // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> {
+            //   if condition() { a } else { b }
+            // }
+            // ```
+            //
+            // Here the captured lifetime is the intersection of `'a` and
+            // `'b`, which we can't quite express.
+
+            // We can at least report a really cryptic error for now.
+            note_and_explain_region(
+                tcx,
+                &mut err,
+                generic_param_scope,
+                &format!("hidden type `{hidden_ty}` captures "),
+                hidden_region,
+                "",
+                None,
+            );
+        }
+    }
+
+    err
+}
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index a3cf588da1c..9f55939c165 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -53,6 +53,7 @@ use type_variable::TypeVariableOrigin;
 
 pub mod at;
 pub mod canonical;
+mod context;
 pub mod error_reporting;
 pub mod free_regions;
 mod freshen;
@@ -684,8 +685,8 @@ impl<'tcx> InferOk<'tcx, ()> {
 }
 
 impl<'tcx> InferCtxt<'tcx> {
-    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
-        self.tcx.dcx()
+    pub fn dcx(&self) -> DiagCtxtHandle<'_> {
+        self.tcx.dcx().taintable_handle(&self.tainted_by_errors)
     }
 
     pub fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
@@ -767,19 +768,9 @@ impl<'tcx> InferCtxt<'tcx> {
             .collect()
     }
 
-    pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, expected: T, actual: T) -> bool
-    where
-        T: at::ToTrace<'tcx>,
-    {
-        let origin = &ObligationCause::dummy();
-        self.probe(|_| {
-            // We're only answering whether there could be a subtyping relation, and with
-            // opaque types, "there could be one", via registering a hidden type.
-            self.at(origin, param_env).sub(DefineOpaqueTypes::Yes, expected, actual).is_ok()
-        })
-    }
-
-    pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool
+    // FIXME(-Znext-solver): Get rid of this method, it's never correct. Either that,
+    // or we need to process the obligations.
+    pub fn can_eq_shallow<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool
     where
         T: at::ToTrace<'tcx>,
     {
@@ -1089,19 +1080,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// inference variables, regionck errors).
     #[must_use = "this method does not have any side effects"]
     pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
-        if let Some(guar) = self.tainted_by_errors.get() {
-            Some(guar)
-        } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation {
-            // Errors reported since this infcx was made. Lint errors are
-            // excluded to avoid some being swallowed in the presence of
-            // non-lint errors. (It's arguable whether or not this exclusion is
-            // important.)
-            let guar = self.dcx().has_errors().unwrap();
-            self.set_tainted_by_errors(guar);
-            Some(guar)
-        } else {
-            None
-        }
+        self.tainted_by_errors.get()
     }
 
     /// Set the "tainted by errors" flag to true. We call this when we
@@ -1328,8 +1307,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     bug!("`{value:?}` is not fully resolved");
                 }
                 if value.has_infer_regions() {
-                    let guar =
-                        self.tcx.dcx().delayed_bug(format!("`{value:?}` is not fully resolved"));
+                    let guar = self.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 }
                     }))
@@ -1607,7 +1585,7 @@ impl<'tcx> InferCtxt<'tcx> {
     }
 }
 
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     // [Note-Type-error-reporting]
     // An invariant is that anytime the expected or actual type is Error (the special
     // error type, meaning that an error occurred when typechecking this expression),
@@ -1623,9 +1601,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         sp: Span,
         mk_diag: M,
         actual_ty: Ty<'tcx>,
-    ) -> Diag<'tcx>
+    ) -> Diag<'a>
     where
-        M: FnOnce(String) -> Diag<'tcx>,
+        M: FnOnce(String) -> Diag<'a>,
     {
         let actual_ty = self.resolve_vars_if_possible(actual_ty);
         debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
@@ -1646,7 +1624,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         err: TypeError<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         self.report_and_explain_type_error(TypeTrace::types(cause, true, expected, actual), err)
     }
 
@@ -1656,7 +1634,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected: ty::Const<'tcx>,
         actual: ty::Const<'tcx>,
         err: TypeError<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         self.report_and_explain_type_error(TypeTrace::consts(cause, true, expected, actual), err)
     }
 }
diff --git a/compiler/rustc_infer/src/infer/opaque_types/mod.rs b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
index b8dd501a721..7c764cccc47 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/mod.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/mod.rs
@@ -156,7 +156,7 @@ impl<'tcx> InferCtxt<'tcx> {
                     if self.can_define_opaque_ty(b_def_id)
                         && self.tcx.is_type_alias_impl_trait(b_def_id)
                     {
-                        self.tcx.dcx().emit_err(OpaqueHiddenTypeDiag {
+                        self.dcx().emit_err(OpaqueHiddenTypeDiag {
                             span,
                             hidden_type: self.tcx.def_span(b_def_id),
                             opaque_type: self.tcx.def_span(def_id),
diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs
deleted file mode 100644
index 6bab3ad6ba3..00000000000
--- a/compiler/rustc_infer/src/infer/outlives/components.rs
+++ /dev/null
@@ -1,266 +0,0 @@
-// The outlines relation `T: 'a` or `'a: 'b`. This code frequently
-// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
-// RFC for reference.
-
-use rustc_data_structures::sso::SsoHashSet;
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{GenericArg, GenericArgKind};
-use smallvec::{smallvec, SmallVec};
-
-#[derive(Debug)]
-pub enum Component<'tcx> {
-    Region(ty::Region<'tcx>),
-    Param(ty::ParamTy),
-    Placeholder(ty::PlaceholderType),
-    UnresolvedInferenceVariable(ty::InferTy),
-
-    // Projections like `T::Foo` are tricky because a constraint like
-    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
-    // where-clause that says `T::Foo: 'a`, or the defining trait may
-    // include a bound like `type Foo: 'static`, or -- in the most
-    // conservative way -- we can prove that `T: 'a` (more generally,
-    // that all components in the projection outlive `'a`). This code
-    // is not in a position to judge which is the best technique, so
-    // we just product the projection as a component and leave it to
-    // the consumer to decide (but see `EscapingProjection` below).
-    Alias(ty::AliasTy<'tcx>),
-
-    // In the case where a projection has escaping regions -- meaning
-    // regions bound within the type itself -- we always use
-    // the most conservative rule, which requires that all components
-    // outlive the bound. So for example if we had a type like this:
-    //
-    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
-    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
-    //
-    // then the inner projection (underlined) has an escaping region
-    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
-    // outlives `'b: 'c`, and we don't consider whether the trait
-    // declares that `Foo: 'static` etc. Therefore, we just return the
-    // free components of such a projection (in this case, `'b`).
-    //
-    // However, in the future, we may want to get smarter, and
-    // actually return a "higher-ranked projection" here. Therefore,
-    // we mark that these components are part of an escaping
-    // projection, so that implied bounds code can avoid relying on
-    // them. This gives us room to improve the regionck reasoning in
-    // the future without breaking backwards compat.
-    EscapingAlias(Vec<Component<'tcx>>),
-}
-
-/// Push onto `out` all the things that must outlive `'a` for the condition
-/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
-pub fn push_outlives_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty0: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-) {
-    let mut visited = SsoHashSet::new();
-    compute_components(tcx, ty0, out, &mut visited);
-    debug!("components({:?}) = {:?}", ty0, out);
-}
-
-fn compute_components<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    // Descend through the types, looking for the various "base"
-    // components and collecting them into `out`. This is not written
-    // with `collect()` because of the need to sometimes skip subtrees
-    // in the `subtys` iterator (e.g., when encountering a
-    // projection).
-    match *ty.kind() {
-            ty::FnDef(_, args) => {
-                // HACK(eddyb) ignore lifetimes found shallowly in `args`.
-                // This is inconsistent with `ty::Adt` (including all args)
-                // and with `ty::Closure` (ignoring all args other than
-                // upvars, of which a `ty::FnDef` doesn't have any), but
-                // consistent with previous (accidental) behavior.
-                // See https://github.com/rust-lang/rust/issues/70917
-                // for further background and discussion.
-                for child in args {
-                    match child.unpack() {
-                        GenericArgKind::Type(ty) => {
-                            compute_components(tcx, ty, out, visited);
-                        }
-                        GenericArgKind::Lifetime(_) => {}
-                        GenericArgKind::Const(_) => {
-                            compute_components_recursive(tcx, child, out, visited);
-                        }
-                    }
-                }
-            }
-
-            ty::Pat(element, _) |
-            ty::Array(element, _) => {
-                // Don't look into the len const as it doesn't affect regions
-                compute_components(tcx, element, out, visited);
-            }
-
-            ty::Closure(_, args) => {
-                let tupled_ty = args.as_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::CoroutineClosure(_, args) => {
-                let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-            }
-
-            ty::Coroutine(_, args) => {
-                // Same as the closure case
-                let tupled_ty = args.as_coroutine().tupled_upvars_ty();
-                compute_components(tcx, tupled_ty, out, visited);
-
-                // We ignore regions in the coroutine interior as we don't
-                // want these to affect region inference
-            }
-
-            // All regions are bound inside a witness
-            ty::CoroutineWitness(..) => (),
-
-            // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
-            // is implied by the environment is done in regionck.
-            ty::Param(p) => {
-                out.push(Component::Param(p));
-            }
-
-            ty::Placeholder(p) => {
-                out.push(Component::Placeholder(p));
-            }
-
-            // For projections, we prefer to generate an obligation like
-            // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
-            // regionck more ways to prove that it holds. However,
-            // regionck is not (at least currently) prepared to deal with
-            // higher-ranked regions that may appear in the
-            // trait-ref. Therefore, if we see any higher-ranked regions,
-            // we simply fallback to the most restrictive rule, which
-            // requires that `Pi: 'a` for all `i`.
-            ty::Alias(_, alias_ty) => {
-                if !alias_ty.has_escaping_bound_vars() {
-                    // best case: no escaping regions, so push the
-                    // projection and skip the subtree (thus generating no
-                    // constraints for Pi). This defers the choice between
-                    // the rules OutlivesProjectionEnv,
-                    // OutlivesProjectionTraitDef, and
-                    // OutlivesProjectionComponents to regionck.
-                    out.push(Component::Alias(alias_ty));
-                } else {
-                    // fallback case: hard code
-                    // OutlivesProjectionComponents. Continue walking
-                    // through and constrain Pi.
-                    let mut subcomponents = smallvec![];
-                    let mut subvisited = SsoHashSet::new();
-                    compute_alias_components_recursive(tcx, ty, &mut subcomponents, &mut subvisited);
-                    out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
-                }
-            }
-
-            // We assume that inference variables are fully resolved.
-            // So, if we encounter an inference variable, just record
-            // the unresolved variable as a component.
-            ty::Infer(infer_ty) => {
-                out.push(Component::UnresolvedInferenceVariable(infer_ty));
-            }
-
-            // Most types do not introduce any region binders, nor
-            // involve any other subtle cases, and so the WF relation
-            // simply constraints any regions referenced directly by
-            // the type and then visits the types that are lexically
-            // contained within. (The comments refer to relevant rules
-            // from RFC1214.)
-            ty::Bool |            // OutlivesScalar
-            ty::Char |            // OutlivesScalar
-            ty::Int(..) |         // OutlivesScalar
-            ty::Uint(..) |        // OutlivesScalar
-            ty::Float(..) |       // OutlivesScalar
-            ty::Never |           // ...
-            ty::Adt(..) |         // OutlivesNominalType
-            ty::Foreign(..) |     // OutlivesNominalType
-            ty::Str |             // OutlivesScalar (ish)
-            ty::Slice(..) |       // ...
-            ty::RawPtr(..) |      // ...
-            ty::Ref(..) |         // OutlivesReference
-            ty::Tuple(..) |       // ...
-            ty::FnPtr(_) |        // OutlivesFunction (*)
-            ty::Dynamic(..) |     // OutlivesObject, OutlivesFragment (*)
-            ty::Bound(..) |
-            ty::Error(_) => {
-                // (*) Function pointers and trait objects are both binders.
-                // In the RFC, this means we would add the bound regions to
-                // the "bound regions list". In our representation, no such
-                // list is maintained explicitly, because bound regions
-                // themselves can be readily identified.
-                compute_components_recursive(tcx, ty.into(), out, visited);
-            }
-        }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-pub(super) fn compute_alias_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    alias_ty: Ty<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
-        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
-    };
-    let opt_variances = if *kind == ty::Opaque { tcx.variances_of(alias_ty.def_id) } else { &[] };
-    for (index, child) in alias_ty.args.iter().enumerate() {
-        if opt_variances.get(index) == Some(&ty::Bivariant) {
-            continue;
-        }
-        if !visited.insert(child) {
-            continue;
-        }
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
-
-/// Collect [Component]s for *all* the args of `parent`.
-///
-/// This should not be used to get the components of `parent` itself.
-/// Use [push_outlives_components] instead.
-fn compute_components_recursive<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    parent: GenericArg<'tcx>,
-    out: &mut SmallVec<[Component<'tcx>; 4]>,
-    visited: &mut SsoHashSet<GenericArg<'tcx>>,
-) {
-    for child in parent.walk_shallow(visited) {
-        match child.unpack() {
-            GenericArgKind::Type(ty) => {
-                compute_components(tcx, ty, out, visited);
-            }
-            GenericArgKind::Lifetime(lt) => {
-                // Ignore higher ranked regions.
-                if !lt.is_bound() {
-                    out.push(Component::Region(lt));
-                }
-            }
-            GenericArgKind::Const(_) => {
-                compute_components_recursive(tcx, child, out, visited);
-            }
-        }
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs
index 48d006e7fbc..89ff4604560 100644
--- a/compiler/rustc_infer/src/infer/outlives/mod.rs
+++ b/compiler/rustc_infer/src/infer/outlives/mod.rs
@@ -8,7 +8,6 @@ use crate::infer::lexical_region_resolve;
 use rustc_middle::traits::query::{NoSolution, OutlivesBound};
 use rustc_middle::ty;
 
-pub mod components;
 pub mod env;
 pub mod for_liveness;
 pub mod obligations;
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 32c790523b6..d82ae7b4fb8 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -59,7 +59,6 @@
 //! might later infer `?U` to something like `&'b u32`, which would
 //! imply that `'b: 'a`.
 
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::outlives::verify::VerifyBoundCx;
 use crate::infer::resolve::OpportunisticRegionResolver;
@@ -75,6 +74,7 @@ use rustc_middle::ty::{
 };
 use rustc_middle::ty::{GenericArgKind, PolyTypeOutlivesPredicate};
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::smallvec;
 
 use super::env::OutlivesEnvironment;
@@ -291,7 +291,7 @@ where
     fn components_must_outlive(
         &mut self,
         origin: infer::SubregionOrigin<'tcx>,
-        components: &[Component<'tcx>],
+        components: &[Component<TyCtxt<'tcx>>],
         region: ty::Region<'tcx>,
         category: ConstraintCategory<'tcx>,
     ) {
@@ -471,7 +471,7 @@ where
         // projection outlive; in some cases, this may add insufficient
         // edges into the inference graph, leading to inference failures
         // even though a satisfactory solution exists.
-        let verify_bound = self.verify_bound.alias_bound(alias_ty, &mut Default::default());
+        let verify_bound = self.verify_bound.alias_bound(alias_ty);
         debug!("alias_must_outlive: pushing {:?}", verify_bound);
         self.delegate.push_verify(origin, GenericKind::Alias(alias_ty), region, verify_bound);
     }
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 7e977b9b954..2392a82025a 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -1,10 +1,8 @@
-use crate::infer::outlives::components::{compute_alias_components_recursive, Component};
 use crate::infer::outlives::env::RegionBoundPairs;
 use crate::infer::region_constraints::VerifyIfEq;
 use crate::infer::{GenericKind, VerifyBound};
-use rustc_data_structures::sso::SsoHashSet;
-use rustc_middle::ty::GenericArg;
 use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
+use rustc_type_ir::outlives::{compute_alias_components_recursive, Component};
 
 use smallvec::smallvec;
 
@@ -99,12 +97,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         self.declared_generic_bounds_from_env_for_erased_ty(erased_alias_ty)
     }
 
-    #[instrument(level = "debug", skip(self, visited))]
-    pub fn alias_bound(
-        &self,
-        alias_ty: ty::AliasTy<'tcx>,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> VerifyBound<'tcx> {
+    #[instrument(level = "debug", skip(self))]
+    pub fn alias_bound(&self, alias_ty: ty::AliasTy<'tcx>) -> VerifyBound<'tcx> {
         let alias_ty_as_ty = alias_ty.to_ty(self.tcx);
 
         // Search the env for where clauses like `P: 'a`.
@@ -130,21 +124,17 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
         // see the extensive comment in projection_must_outlive
         let recursive_bound = {
             let mut components = smallvec![];
-            compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components, visited);
-            self.bound_from_components(&components, visited)
+            compute_alias_components_recursive(self.tcx, alias_ty_as_ty, &mut components);
+            self.bound_from_components(&components)
         };
 
         VerifyBound::AnyBound(env_bounds.chain(definition_bounds).collect()).or(recursive_bound)
     }
 
-    fn bound_from_components(
-        &self,
-        components: &[Component<'tcx>],
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> VerifyBound<'tcx> {
+    fn bound_from_components(&self, components: &[Component<TyCtxt<'tcx>>]) -> VerifyBound<'tcx> {
         let mut bounds = components
             .iter()
-            .map(|component| self.bound_from_single_component(component, visited))
+            .map(|component| self.bound_from_single_component(component))
             // Remove bounds that must hold, since they are not interesting.
             .filter(|bound| !bound.must_hold());
 
@@ -158,8 +148,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
 
     fn bound_from_single_component(
         &self,
-        component: &Component<'tcx>,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
+        component: &Component<TyCtxt<'tcx>>,
     ) -> VerifyBound<'tcx> {
         match *component {
             Component::Region(lt) => VerifyBound::OutlivedBy(lt),
@@ -167,10 +156,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
             Component::Placeholder(placeholder_ty) => {
                 self.param_or_placeholder_bound(Ty::new_placeholder(self.tcx, placeholder_ty))
             }
-            Component::Alias(alias_ty) => self.alias_bound(alias_ty, visited),
-            Component::EscapingAlias(ref components) => {
-                self.bound_from_components(components, visited)
-            }
+            Component::Alias(alias_ty) => self.alias_bound(alias_ty),
+            Component::EscapingAlias(ref components) => self.bound_from_components(components),
             Component::UnresolvedInferenceVariable(v) => {
                 // Ignore this, we presume it will yield an error later, since
                 // if a type variable is not resolved by this point it never
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 0a2e85cc891..c93b89756f9 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -18,11 +18,13 @@
 //! On success, the  LUB/GLB operations return the appropriate bound. The
 //! return value of `Equate` or `Sub` shouldn't really be used.
 
+pub use rustc_next_trait_solver::relate::combine::*;
+
 use super::glb::Glb;
 use super::lub::Lub;
 use super::type_relating::TypeRelating;
+use super::RelateResult;
 use super::StructurallyRelateAliases;
-use super::{RelateResult, TypeRelation};
 use crate::infer::relate;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligation};
@@ -32,7 +34,6 @@ use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, InferConst, Ty, TyCtxt, TypeVisitableExt, Upcast};
 use rustc_middle::ty::{IntType, UintType};
-use rustc_span::Span;
 
 #[derive(Clone)]
 pub struct CombineFields<'infcx, 'tcx> {
@@ -76,7 +77,7 @@ impl<'tcx> InferCtxt<'tcx> {
         b: Ty<'tcx>,
     ) -> RelateResult<'tcx, Ty<'tcx>>
     where
-        R: PredicateEmittingRelation<'tcx>,
+        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
     {
         debug_assert!(!a.has_escaping_bound_vars());
         debug_assert!(!b.has_escaping_bound_vars());
@@ -171,7 +172,7 @@ impl<'tcx> InferCtxt<'tcx> {
         b: ty::Const<'tcx>,
     ) -> RelateResult<'tcx, ty::Const<'tcx>>
     where
-        R: PredicateEmittingRelation<'tcx>,
+        R: PredicateEmittingRelation<InferCtxt<'tcx>>,
     {
         debug!("{}.consts({:?}, {:?})", relation.tag(), a, b);
         debug_assert!(!a.has_escaping_bound_vars());
@@ -323,30 +324,3 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         )
     }
 }
-
-pub trait PredicateEmittingRelation<'tcx>: TypeRelation<TyCtxt<'tcx>> {
-    fn span(&self) -> Span;
-
-    fn param_env(&self) -> ty::ParamEnv<'tcx>;
-
-    /// Whether aliases should be related structurally. This is pretty much
-    /// always `No` unless you're equating in some specific locations of the
-    /// new solver. See the comments in these use-cases for more details.
-    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
-
-    /// Register obligations that must hold in order for this relation to hold
-    fn register_goals(
-        &mut self,
-        obligations: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
-    );
-
-    /// Register predicates that must hold in order for this relation to hold.
-    /// This uses the default `param_env` of the obligation.
-    fn register_predicates(
-        &mut self,
-        obligations: impl IntoIterator<Item: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>>>,
-    );
-
-    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
-    fn register_alias_relate_predicate(&mut self, a: Ty<'tcx>, b: Ty<'tcx>);
-}
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index d6e57d85387..fe3b8d60fb9 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -30,7 +30,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
     /// other usecases (i.e. setting the value of a type var).
     #[instrument(level = "debug", skip(self, relation))]
-    pub fn instantiate_ty_var<R: PredicateEmittingRelation<'tcx>>(
+    pub fn instantiate_ty_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
@@ -178,7 +178,7 @@ impl<'tcx> InferCtxt<'tcx> {
     ///
     /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
     #[instrument(level = "debug", skip(self, relation))]
-    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<'tcx>>(
+    pub(super) fn instantiate_const_var<R: PredicateEmittingRelation<InferCtxt<'tcx>>>(
         &self,
         relation: &mut R,
         target_is_expected: bool,
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index 067004ecaeb..5bb8a113e17 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Glb<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/infer/relate/lattice.rs b/compiler/rustc_infer/src/infer/relate/lattice.rs
index 6cc8d6d910a..46e7466141a 100644
--- a/compiler/rustc_infer/src/infer/relate/lattice.rs
+++ b/compiler/rustc_infer/src/infer/relate/lattice.rs
@@ -30,7 +30,7 @@ use rustc_middle::ty::{self, Ty};
 ///
 /// GLB moves "down" the lattice (to smaller values); LUB moves
 /// "up" the lattice (to bigger values).
-pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<'tcx> {
+pub trait LatticeDir<'f, 'tcx>: PredicateEmittingRelation<InferCtxt<'tcx>> {
     fn infcx(&self) -> &'f InferCtxt<'tcx>;
 
     fn cause(&self) -> &ObligationCause<'tcx>;
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index 2184416b4cc..94c1464817f 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -123,7 +123,7 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for Lub<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index 41cc945492d..dd97dc061fe 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -2,11 +2,13 @@
 //! (except for some relations used for diagnostics and heuristics in the compiler).
 //! As well as the implementation of `Relate` for interned things (`Ty`/`Const`/etc).
 
-pub use rustc_middle::ty::relate::*;
+pub use rustc_middle::ty::relate::RelateResult;
+pub use rustc_next_trait_solver::relate::*;
 
 pub use self::combine::CombineFields;
 pub use self::combine::PredicateEmittingRelation;
 
+#[allow(hidden_glob_reexports)]
 pub(super) mod combine;
 mod generalize;
 mod glb;
diff --git a/compiler/rustc_infer/src/infer/relate/type_relating.rs b/compiler/rustc_infer/src/infer/relate/type_relating.rs
index f2bec9392d5..e206f530519 100644
--- a/compiler/rustc_infer/src/infer/relate/type_relating.rs
+++ b/compiler/rustc_infer/src/infer/relate/type_relating.rs
@@ -1,7 +1,7 @@
 use super::combine::CombineFields;
 use crate::infer::relate::{PredicateEmittingRelation, StructurallyRelateAliases};
 use crate::infer::BoundRegionConversionTime::HigherRankedType;
-use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
+use crate::infer::{DefineOpaqueTypes, InferCtxt, SubregionOrigin};
 use rustc_middle::traits::solve::Goal;
 use rustc_middle::ty::relate::{
     relate_args_invariantly, relate_args_with_variances, Relate, RelateResult, TypeRelation,
@@ -296,7 +296,7 @@ impl<'tcx> TypeRelation<TyCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     }
 }
 
-impl<'tcx> PredicateEmittingRelation<'tcx> for TypeRelating<'_, '_, 'tcx> {
+impl<'tcx> PredicateEmittingRelation<InferCtxt<'tcx>> for TypeRelating<'_, '_, 'tcx> {
     fn span(&self) -> Span {
         self.fields.trace.span()
     }
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index 890e25368bc..7730fe29e09 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -12,15 +12,15 @@ use std::fmt;
 use std::iter;
 
 impl<'tcx> InferCtxt<'tcx> {
-    pub fn report_extra_impl_obligation(
-        &self,
+    pub fn report_extra_impl_obligation<'a>(
+        &'a self,
         error_span: Span,
         impl_item_def_id: LocalDefId,
         trait_item_def_id: DefId,
         requirement: &dyn fmt::Display,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let mut err = struct_span_code_err!(
-            self.tcx.dcx(),
+            self.dcx(),
             error_span,
             E0276,
             "impl has stricter requirements than trait"
diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs
index ab4148faaab..f54d0418595 100644
--- a/compiler/rustc_infer/src/traits/util.rs
+++ b/compiler/rustc_infer/src/traits/util.rs
@@ -1,11 +1,10 @@
-use smallvec::smallvec;
-
-use crate::infer::outlives::components::{push_outlives_components, Component};
 use crate::traits::{self, Obligation, ObligationCauseCode, PredicateObligation};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
+use rustc_middle::ty::ToPolyTraitRef;
+use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::symbol::Ident;
 use rustc_span::Span;
+pub use rustc_type_ir::elaborate::*;
 
 pub fn anonymize_predicate<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -63,51 +62,9 @@ impl<'tcx> Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> {
     }
 }
 
-///////////////////////////////////////////////////////////////////////////
-// `Elaboration` iterator
-///////////////////////////////////////////////////////////////////////////
-
-/// "Elaboration" is the process of identifying all the predicates that
-/// are implied by a source predicate. Currently, this basically means
-/// walking the "supertraits" and other similar assumptions. For example,
-/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
-/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
-/// `T: Foo`, then we know that `T: 'static`.
-pub struct Elaborator<'tcx, O> {
-    stack: Vec<O>,
-    visited: PredicateSet<'tcx>,
-    mode: Filter,
-}
-
-enum Filter {
-    All,
-    OnlySelf,
-    OnlySelfThatDefines(Ident),
-}
-
-/// Describes how to elaborate an obligation into a sub-obligation.
-///
 /// For [`Obligation`], a sub-obligation is combined with the current obligation's
-/// param-env and cause code. For [`ty::Predicate`], none of this is needed, since
-/// there is no param-env or cause code to copy over.
-pub trait Elaboratable<'tcx> {
-    fn predicate(&self) -> ty::Predicate<'tcx>;
-
-    // Makes a new `Self` but with a different clause that comes from elaboration.
-    fn child(&self, clause: ty::Clause<'tcx>) -> Self;
-
-    // Makes a new `Self` but with a different clause and a different cause
-    // code (if `Self` has one, such as [`PredicateObligation`]).
-    fn child_with_derived_cause(
-        &self,
-        clause: ty::Clause<'tcx>,
-        span: Span,
-        parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-        index: usize,
-    ) -> Self;
-}
-
-impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
+/// param-env and cause code.
+impl<'tcx> Elaboratable<TyCtxt<'tcx>> for PredicateObligation<'tcx> {
     fn predicate(&self) -> ty::Predicate<'tcx> {
         self.predicate
     }
@@ -145,324 +102,37 @@ impl<'tcx> Elaboratable<'tcx> for PredicateObligation<'tcx> {
     }
 }
 
-impl<'tcx> Elaboratable<'tcx> for ty::Predicate<'tcx> {
-    fn predicate(&self) -> ty::Predicate<'tcx> {
-        *self
-    }
-
-    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
-        clause.as_predicate()
-    }
-
-    fn child_with_derived_cause(
-        &self,
-        clause: ty::Clause<'tcx>,
-        _span: Span,
-        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-        _index: usize,
-    ) -> Self {
-        clause.as_predicate()
-    }
-}
-
-impl<'tcx> Elaboratable<'tcx> for (ty::Predicate<'tcx>, Span) {
-    fn predicate(&self) -> ty::Predicate<'tcx> {
-        self.0
-    }
-
-    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
-        (clause.as_predicate(), self.1)
-    }
-
-    fn child_with_derived_cause(
-        &self,
-        clause: ty::Clause<'tcx>,
-        _span: Span,
-        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-        _index: usize,
-    ) -> Self {
-        (clause.as_predicate(), self.1)
-    }
-}
-
-impl<'tcx> Elaboratable<'tcx> for (ty::Clause<'tcx>, Span) {
-    fn predicate(&self) -> ty::Predicate<'tcx> {
-        self.0.as_predicate()
-    }
-
-    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
-        (clause, self.1)
-    }
-
-    fn child_with_derived_cause(
-        &self,
-        clause: ty::Clause<'tcx>,
-        _span: Span,
-        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-        _index: usize,
-    ) -> Self {
-        (clause, self.1)
-    }
-}
-
-impl<'tcx> Elaboratable<'tcx> for ty::Clause<'tcx> {
-    fn predicate(&self) -> ty::Predicate<'tcx> {
-        self.as_predicate()
-    }
-
-    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
-        clause
-    }
-
-    fn child_with_derived_cause(
-        &self,
-        clause: ty::Clause<'tcx>,
-        _span: Span,
-        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
-        _index: usize,
-    ) -> Self {
-        clause
-    }
-}
-
-pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
-    tcx: TyCtxt<'tcx>,
-    obligations: impl IntoIterator<Item = O>,
-) -> Elaborator<'tcx, O> {
-    let mut elaborator =
-        Elaborator { stack: Vec::new(), visited: PredicateSet::new(tcx), mode: Filter::All };
-    elaborator.extend_deduped(obligations);
-    elaborator
-}
-
-impl<'tcx, O: Elaboratable<'tcx>> Elaborator<'tcx, O> {
-    fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
-        // Only keep those bounds that we haven't already seen.
-        // This is necessary to prevent infinite recursion in some
-        // cases. One common case is when people define
-        // `trait Sized: Sized { }` rather than `trait Sized { }`.
-        // let visited = &mut self.visited;
-        self.stack.extend(obligations.into_iter().filter(|o| self.visited.insert(o.predicate())));
-    }
-
-    /// Filter to only the supertraits of trait predicates, i.e. only the predicates
-    /// that have `Self` as their self type, instead of all implied predicates.
-    pub fn filter_only_self(mut self) -> Self {
-        self.mode = Filter::OnlySelf;
-        self
-    }
-
-    /// Filter to only the supertraits of trait predicates that define the assoc_ty.
-    pub fn filter_only_self_that_defines(mut self, assoc_ty: Ident) -> Self {
-        self.mode = Filter::OnlySelfThatDefines(assoc_ty);
-        self
-    }
-
-    fn elaborate(&mut self, elaboratable: &O) {
-        let tcx = self.visited.tcx;
-
-        // We only elaborate clauses.
-        let Some(clause) = elaboratable.predicate().as_clause() else {
-            return;
-        };
-
-        let bound_clause = clause.kind();
-        match bound_clause.skip_binder() {
-            ty::ClauseKind::Trait(data) => {
-                // Negative trait bounds do not imply any supertrait bounds
-                if data.polarity != ty::PredicatePolarity::Positive {
-                    return;
-                }
-                // Get predicates implied by the trait, or only super predicates if we only care about self predicates.
-                let predicates = match self.mode {
-                    Filter::All => tcx.implied_predicates_of(data.def_id()),
-                    Filter::OnlySelf => tcx.super_predicates_of(data.def_id()),
-                    Filter::OnlySelfThatDefines(ident) => {
-                        tcx.super_predicates_that_define_assoc_item((data.def_id(), ident))
-                    }
-                };
-
-                let obligations =
-                    predicates.predicates.iter().enumerate().map(|(index, &(clause, span))| {
-                        elaboratable.child_with_derived_cause(
-                            clause.instantiate_supertrait(tcx, bound_clause.rebind(data.trait_ref)),
-                            span,
-                            bound_clause.rebind(data),
-                            index,
-                        )
-                    });
-                debug!(?data, ?obligations, "super_predicates");
-                self.extend_deduped(obligations);
-            }
-            ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
-                // We know that `T: 'a` for some type `T`. We can
-                // often elaborate this. For example, if we know that
-                // `[U]: 'a`, that implies that `U: 'a`. Similarly, if
-                // we know `&'a U: 'b`, then we know that `'a: 'b` and
-                // `U: 'b`.
-                //
-                // We can basically ignore bound regions here. So for
-                // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to
-                // `'a: 'b`.
-
-                // Ignore `for<'a> T: 'a` -- we might in the future
-                // consider this as evidence that `T: 'static`, but
-                // I'm a bit wary of such constructions and so for now
-                // I want to be conservative. --nmatsakis
-                if r_min.is_bound() {
-                    return;
-                }
-
-                let mut components = smallvec![];
-                push_outlives_components(tcx, ty_max, &mut components);
-                self.extend_deduped(
-                    components
-                        .into_iter()
-                        .filter_map(|component| match component {
-                            Component::Region(r) => {
-                                if r.is_bound() {
-                                    None
-                                } else {
-                                    Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(
-                                        r, r_min,
-                                    )))
-                                }
-                            }
-
-                            Component::Param(p) => {
-                                let ty = Ty::new_param(tcx, p.index, p.name);
-                                Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
-                            }
-
-                            Component::Placeholder(p) => {
-                                let ty = Ty::new_placeholder(tcx, p);
-                                Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, r_min)))
-                            }
-
-                            Component::UnresolvedInferenceVariable(_) => None,
-
-                            Component::Alias(alias_ty) => {
-                                // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
-                                // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
-                                Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
-                                    alias_ty.to_ty(tcx),
-                                    r_min,
-                                )))
-                            }
-
-                            Component::EscapingAlias(_) => {
-                                // We might be able to do more here, but we don't
-                                // want to deal with escaping vars right now.
-                                None
-                            }
-                        })
-                        .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(tcx))),
-                );
-            }
-            ty::ClauseKind::RegionOutlives(..) => {
-                // Nothing to elaborate from `'a: 'b`.
-            }
-            ty::ClauseKind::WellFormed(..) => {
-                // Currently, we do not elaborate WF predicates,
-                // although we easily could.
-            }
-            ty::ClauseKind::Projection(..) => {
-                // Nothing to elaborate in a projection predicate.
-            }
-            ty::ClauseKind::ConstEvaluatable(..) => {
-                // Currently, we do not elaborate const-evaluatable
-                // predicates.
-            }
-            ty::ClauseKind::ConstArgHasType(..) => {
-                // Nothing to elaborate
-            }
-        }
-    }
-}
-
-impl<'tcx, O: Elaboratable<'tcx>> Iterator for Elaborator<'tcx, O> {
-    type Item = O;
-
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        (self.stack.len(), None)
-    }
-
-    fn next(&mut self) -> Option<Self::Item> {
-        // Extract next item from top-most stack frame, if any.
-        if let Some(obligation) = self.stack.pop() {
-            self.elaborate(&obligation);
-            Some(obligation)
-        } else {
-            None
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Supertrait iterator
-///////////////////////////////////////////////////////////////////////////
-
-pub fn supertraits<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: ty::PolyTraitRef<'tcx>,
-) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> {
-    elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits()
-}
-
-pub fn transitive_bounds<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
-) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> {
-    elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx)))
-        .filter_only_self()
-        .filter_to_traits()
-}
-
 /// A specialized variant of `elaborate` that only elaborates trait references that may
 /// define the given associated item with the name `assoc_name`. It uses the
-/// `super_predicates_that_define_assoc_item` query to avoid enumerating super-predicates that
+/// `explicit_supertraits_containing_assoc_item` query to avoid enumerating super-predicates that
 /// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or
 /// `T::Item` and helps to avoid cycle errors (see e.g. #35237).
 pub fn transitive_bounds_that_define_assoc_item<'tcx>(
     tcx: TyCtxt<'tcx>,
     trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>,
     assoc_name: Ident,
-) -> FilterToTraits<Elaborator<'tcx, ty::Predicate<'tcx>>> {
-    elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx)))
-        .filter_only_self_that_defines(assoc_name)
-        .filter_to_traits()
-}
-
-///////////////////////////////////////////////////////////////////////////
-// Other
-///////////////////////////////////////////////////////////////////////////
-
-impl<'tcx> Elaborator<'tcx, ty::Predicate<'tcx>> {
-    fn filter_to_traits(self) -> FilterToTraits<Self> {
-        FilterToTraits { base_iterator: self }
-    }
-}
-
-/// A filter around an iterator of predicates that makes it yield up
-/// just trait references.
-pub struct FilterToTraits<I> {
-    base_iterator: I,
-}
+) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> {
+    let mut seen = FxHashSet::default();
+    let mut stack: Vec<_> = trait_refs.collect();
+
+    std::iter::from_fn(move || {
+        while let Some(trait_ref) = stack.pop() {
+            if !seen.insert(tcx.anonymize_bound_vars(trait_ref)) {
+                continue;
+            }
 
-impl<'tcx, I: Iterator<Item = ty::Predicate<'tcx>>> Iterator for FilterToTraits<I> {
-    type Item = ty::PolyTraitRef<'tcx>;
+            stack.extend(
+                tcx.explicit_supertraits_containing_assoc_item((trait_ref.def_id(), assoc_name))
+                    .instantiate_own_identity()
+                    .map(|(clause, _)| clause.instantiate_supertrait(tcx, trait_ref))
+                    .filter_map(|clause| clause.as_trait_clause())
+                    // FIXME: Negative supertraits are elaborated here lol
+                    .map(|trait_pred| trait_pred.to_poly_trait_ref()),
+            );
 
-    fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> {
-        while let Some(pred) = self.base_iterator.next() {
-            if let Some(data) = pred.as_trait_clause() {
-                return Some(data.map_bound(|t| t.trait_ref));
-            }
+            return Some(trait_ref);
         }
-        None
-    }
 
-    fn size_hint(&self) -> (usize, Option<usize>) {
-        let (_, upper) = self.base_iterator.size_hint();
-        (0, upper)
-    }
+        None
+    })
 }
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 38f64ebb04e..e37b30749ab 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -16,7 +16,7 @@ pub mod util;
 pub use callbacks::setup_callbacks;
 pub use interface::{run_compiler, Config};
 pub use passes::DEFAULT_QUERY_PROVIDERS;
-pub use queries::Queries;
+pub use queries::{Linker, Queries};
 
 #[cfg(test)]
 mod tests;
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index cfd4304e893..821e8ee7ba5 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -65,12 +65,6 @@ impl<'a, 'tcx> QueryResult<'a, &'tcx GlobalCtxt<'tcx>> {
     }
 }
 
-impl<T> Default for Query<T> {
-    fn default() -> Self {
-        Query { result: RefCell::new(None) }
-    }
-}
-
 pub struct Queries<'tcx> {
     compiler: &'tcx Compiler,
     gcx_cell: OnceLock<GlobalCtxt<'tcx>>,
@@ -90,8 +84,8 @@ impl<'tcx> Queries<'tcx> {
             gcx_cell: OnceLock::new(),
             arena: WorkerLocal::new(|_| Arena::default()),
             hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()),
-            parse: Default::default(),
-            gcx: Default::default(),
+            parse: Query { result: RefCell::new(None) },
+            gcx: Query { result: RefCell::new(None) },
         }
     }
 
@@ -116,23 +110,6 @@ impl<'tcx> Queries<'tcx> {
             )
         })
     }
-
-    pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> {
-        self.global_ctxt()?.enter(|tcx| {
-            let ongoing_codegen = passes::start_codegen(&*self.compiler.codegen_backend, tcx)?;
-
-            Ok(Linker {
-                dep_graph: tcx.dep_graph.clone(),
-                output_filenames: tcx.output_filenames(()).clone(),
-                crate_hash: if tcx.needs_crate_hash() {
-                    Some(tcx.crate_hash(LOCAL_CRATE))
-                } else {
-                    None
-                },
-                ongoing_codegen,
-            })
-        })
-    }
 }
 
 pub struct Linker {
@@ -144,6 +121,36 @@ pub struct Linker {
 }
 
 impl Linker {
+    pub fn codegen_and_build_linker(
+        tcx: TyCtxt<'_>,
+        codegen_backend: &dyn CodegenBackend,
+    ) -> Result<Linker> {
+        let ongoing_codegen = passes::start_codegen(codegen_backend, tcx)?;
+
+        // This must run after monomorphization so that all generic types
+        // have been instantiated.
+        if tcx.sess.opts.unstable_opts.print_type_sizes {
+            tcx.sess.code_stats.print_type_sizes();
+        }
+
+        if tcx.sess.opts.unstable_opts.print_vtable_sizes {
+            let crate_name = tcx.crate_name(LOCAL_CRATE);
+
+            tcx.sess.code_stats.print_vtable_sizes(crate_name);
+        }
+
+        Ok(Linker {
+            dep_graph: tcx.dep_graph.clone(),
+            output_filenames: tcx.output_filenames(()).clone(),
+            crate_hash: if tcx.needs_crate_hash() {
+                Some(tcx.crate_hash(LOCAL_CRATE))
+            } else {
+                None
+            },
+            ongoing_codegen,
+        })
+    }
+
     pub fn link(self, sess: &Session, codegen_backend: &dyn CodegenBackend) -> Result<()> {
         let (codegen_results, work_products) =
             codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
@@ -197,7 +204,7 @@ impl Compiler {
         F: for<'tcx> FnOnce(&'tcx Queries<'tcx>) -> T,
     {
         // Must declare `_timer` first so that it is dropped after `queries`.
-        let mut _timer = None;
+        let _timer;
         let queries = Queries::new(self);
         let ret = f(&queries);
 
@@ -220,7 +227,7 @@ impl Compiler {
 
         // The timer's lifetime spans the dropping of `queries`, which contains
         // the global context.
-        _timer = Some(self.sess.timer("free_global_ctxt"));
+        _timer = self.sess.timer("free_global_ctxt");
         if let Err((path, error)) = queries.finish() {
             self.sess.dcx().emit_fatal(errors::FailedWritingFile { path: &path, error });
         }
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 6ffc518097e..e2ba75dfd19 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -2,14 +2,22 @@
 use crate::interface::{initialize_checked_jobserver, parse_cfg};
 use rustc_data_structures::profiling::TimePassesFormat;
 use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
+use rustc_session::config::{build_configuration, build_session_options, rustc_optgroups};
 use rustc_session::config::{
-    build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
-    CollapseMacroDebuginfo, CoverageLevel, CoverageOptions, DebugInfo, DumpMonoStatsFormat,
-    ErrorOutputType, ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold,
-    Input, InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail,
-    LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey,
-    PacRet, Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath,
-    SymbolManglingVersion, WasiExecModel,
+    BranchProtection, CFGuard, Cfg, CollapseMacroDebuginfo, CoverageLevel, CoverageOptions,
+    DebugInfo, DumpMonoStatsFormat, ErrorOutputType,
+};
+use rustc_session::config::{
+    ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, Input,
+    InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto,
+};
+use rustc_session::config::{
+    LocationDetail, LtoCli, NextSolverConfig, OomStrategy, Options, OutFileName, OutputType,
+    OutputTypes, PAuthKey, PacRet, Passes, PatchableFunctionEntry,
+};
+use rustc_session::config::{
+    Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion,
+    WasiExecModel,
 };
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
@@ -749,7 +757,6 @@ fn test_unstable_options_tracking_hash() {
     // tidy-alphabetical-start
     tracked!(allow_features, Some(vec![String::from("lang_items")]));
     tracked!(always_encode_mir, true);
-    tracked!(asm_comments, true);
     tracked!(assume_incomplete_release, true);
     tracked!(binary_dep_depinfo, true);
     tracked!(box_noalias, false);
@@ -813,6 +820,11 @@ fn test_unstable_options_tracking_hash() {
     tracked!(packed_bundled_libs, true);
     tracked!(panic_abort_tests, true);
     tracked!(panic_in_drop, PanicStrategy::Abort);
+    tracked!(
+        patchable_function_entry,
+        PatchableFunctionEntry::from_total_and_prefix_nops(10, 5)
+            .expect("total must be greater than or equal to prefix")
+    );
     tracked!(plt, Some(true));
     tracked!(polonius, Polonius::Legacy);
     tracked!(precise_enum_drop_elaboration, false);
@@ -849,6 +861,7 @@ fn test_unstable_options_tracking_hash() {
     tracked!(uninit_const_chunk_threshold, 123);
     tracked!(unleash_the_miri_inside_of_you, true);
     tracked!(use_ctors_section, Some(true));
+    tracked!(verbose_asm, true);
     tracked!(verify_llvm_ir, true);
     tracked!(virtual_function_elimination, true);
     tracked!(wasi_exec_model, Some(WasiExecModel::Reactor));
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 46cf87d1e3c..f048f6fe8ad 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -56,8 +56,6 @@ lint_builtin_asm_labels = avoid using named labels in inline assembly
     .help = only local labels of the form `<number>:` should be used in inline asm
     .note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
 
-lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty}
-
 lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
     .previous_decl_label = `{$orig}` previously declared here
     .mismatch_label = this signature doesn't match the previous declaration
@@ -441,6 +439,9 @@ lint_lintpass_by_hand = implementing `LintPass` by hand
 lint_macro_expanded_macro_exports_accessed_by_absolute_paths = macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths
     .note = the macro is defined here
 
+lint_macro_expr_fragment_specifier_2024_migration =
+    the `expr` fragment specifier will accept more expressions in the 2024 edition
+    .suggestion = to keep the existing behavior, use the `expr_2021` fragment specifier
 lint_macro_is_private = macro `{$ident}` is private
 
 lint_macro_rule_never_used = rule #{$n} of macro `{$name}` is never used
diff --git a/compiler/rustc_lint/src/async_fn_in_trait.rs b/compiler/rustc_lint/src/async_fn_in_trait.rs
index 40778542c75..6daee95dda6 100644
--- a/compiler/rustc_lint/src/async_fn_in_trait.rs
+++ b/compiler/rustc_lint/src/async_fn_in_trait.rs
@@ -3,7 +3,7 @@ use crate::LateContext;
 use crate::LateLintPass;
 use rustc_hir as hir;
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_trait_selection::traits::error_reporting::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait;
+use rustc_trait_selection::error_reporting::traits::suggestions::suggest_desugaring_async_fn_to_impl_future_in_trait;
 
 declare_lint! {
     /// The `async_fn_in_trait` lint detects use of `async fn` in the
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 98318cd14d9..79c8046f9b7 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -24,9 +24,9 @@ use crate::fluent_generated as fluent;
 use crate::{
     errors::BuiltinEllipsisInclusiveRangePatterns,
     lints::{
-        BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
-        BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed,
-        BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
+        BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
+        BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
+        BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
         BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
         BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
         BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
@@ -56,7 +56,6 @@ use rustc_middle::bug;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::GenericArgKind;
 use rustc_middle::ty::TypeVisitableExt;
 use rustc_middle::ty::Upcast;
 use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
@@ -135,80 +134,6 @@ impl EarlyLintPass for WhileTrue {
 }
 
 declare_lint! {
-    /// The `box_pointers` lints use of the Box type.
-    ///
-    /// ### Example
-    ///
-    /// ```rust,compile_fail
-    /// #![deny(box_pointers)]
-    /// struct Foo {
-    ///     x: Box<i32>,
-    /// }
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// This lint is mostly historical, and not particularly useful. `Box<T>`
-    /// used to be built into the language, and the only way to do heap
-    /// allocation. Today's Rust can call into other allocators, etc.
-    BOX_POINTERS,
-    Allow,
-    "use of owned (Box type) heap memory"
-}
-
-declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
-
-impl BoxPointers {
-    fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
-        for leaf in ty.walk() {
-            if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
-                && leaf_ty.is_box()
-            {
-                cx.emit_span_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
-            }
-        }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for BoxPointers {
-    fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
-        match it.kind {
-            hir::ItemKind::Fn(..)
-            | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::Enum(..)
-            | hir::ItemKind::Struct(..)
-            | hir::ItemKind::Union(..) => self.check_heap_type(
-                cx,
-                it.span,
-                cx.tcx.type_of(it.owner_id).instantiate_identity(),
-            ),
-            _ => (),
-        }
-
-        // If it's a struct, we also have to check the fields' types
-        match it.kind {
-            hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
-                for field in struct_def.fields() {
-                    self.check_heap_type(
-                        cx,
-                        field.span,
-                        cx.tcx.type_of(field.def_id).instantiate_identity(),
-                    );
-                }
-            }
-            _ => (),
-        }
-    }
-
-    fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
-        let ty = cx.typeck_results().node_type(e.hir_id);
-        self.check_heap_type(cx, e.span, ty);
-    }
-}
-
-declare_lint! {
     /// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`
     /// instead of `Struct { x }` in a pattern.
     ///
@@ -1640,7 +1565,6 @@ declare_lint_pass!(
     /// which are used by other parts of the compiler.
     SoftLints => [
         WHILE_TRUE,
-        BOX_POINTERS,
         NON_SHORTHAND_FIELD_PATTERNS,
         UNSAFE_CODE,
         MISSING_DOCS,
diff --git a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs
index c69e680cb64..da36f68fca9 100644
--- a/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs
+++ b/compiler/rustc_lint/src/context/diagnostics/check_cfg.rs
@@ -261,10 +261,16 @@ pub(super) fn unexpected_cfg_value(
         lints::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name }
     };
 
-    // We don't want to suggest adding values to well known names
-    // since those are defined by rustc it-self. Users can still
-    // do it if they want, but should not encourage them.
-    let is_cfg_a_well_know_name = sess.psess.check_config.well_known_names.contains(&name);
+    // We don't want to encourage people to add values to a well-known names, as these are
+    // defined by rustc/Rust itself. Users can still do this if they wish, but should not be
+    // encouraged to do so.
+    let can_suggest_adding_value = !sess.psess.check_config.well_known_names.contains(&name)
+        // Except when working on rustc or the standard library itself, in which case we want to
+        // suggest adding these cfgs to the "normal" place because of bootstraping reasons. As a
+        // basic heuristic, we use the "cheat" unstable feature enable method and the
+        // non-ui-testing enabled option.
+        || (matches!(sess.psess.unstable_features, rustc_feature::UnstableFeatures::Cheat)
+            && !sess.opts.unstable_opts.ui_testing);
 
     let inst = |escape_quotes| to_check_cfg_arg(name, value.map(|(v, _s)| v), escape_quotes);
 
@@ -275,14 +281,14 @@ pub(super) fn unexpected_cfg_value(
             } else {
                 Some(lints::unexpected_cfg_value::CargoHelp::DefineFeatures)
             }
-        } else if !is_cfg_a_well_know_name {
+        } else if can_suggest_adding_value {
             Some(lints::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst)))
         } else {
             None
         };
         lints::unexpected_cfg_value::InvocationHelp::Cargo(help)
     } else {
-        let help = if !is_cfg_a_well_know_name {
+        let help = if can_suggest_adding_value {
             Some(lints::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)))
         } else {
             None
diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs
index 9110cccdc46..772cc2ff8b9 100644
--- a/compiler/rustc_lint/src/internal.rs
+++ b/compiler/rustc_lint/src/internal.rs
@@ -88,7 +88,7 @@ declare_lint_pass!(QueryStability => [POTENTIAL_QUERY_INSTABILITY]);
 impl LateLintPass<'_> for QueryStability {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         let Some((span, def_id, args)) = typeck_results_of_method_fn(cx, expr) else { return };
-        if let Ok(Some(instance)) = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, args) {
+        if let Ok(Some(instance)) = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, args) {
             let def_id = instance.def_id();
             if cx.tcx.has_attr(def_id, sym::rustc_lint_query_instability) {
                 cx.emit_span_lint(
@@ -393,7 +393,7 @@ impl LateLintPass<'_> for Diagnostics {
         };
 
         // Is the callee marked with `#[rustc_lint_diagnostics]`?
-        let has_attr = ty::Instance::resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
+        let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
             .ok()
             .flatten()
             .is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 7dae2de7bfb..868a44a980a 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -60,6 +60,7 @@ mod late;
 mod let_underscore;
 mod levels;
 mod lints;
+mod macro_expr_fragment_specifier_2024_migration;
 mod map_unit_fn;
 mod methods;
 mod multiple_supertrait_upcastable;
@@ -97,6 +98,7 @@ use impl_trait_overcaptures::ImplTraitOvercaptures;
 use internal::*;
 use invalid_from_utf8::*;
 use let_underscore::*;
+use macro_expr_fragment_specifier_2024_migration::*;
 use map_unit_fn::*;
 use methods::*;
 use multiple_supertrait_upcastable::*;
@@ -170,6 +172,7 @@ early_lint_methods!(
             IncompleteInternalFeatures: IncompleteInternalFeatures,
             RedundantSemicolons: RedundantSemicolons,
             UnusedDocComment: UnusedDocComment,
+            Expr2024: Expr2024,
         ]
     ]
 );
@@ -187,7 +190,6 @@ late_lint_methods!(
             ImproperCTypesDefinitions: ImproperCTypesDefinitions,
             InvalidFromUtf8: InvalidFromUtf8,
             VariantSizeDifferences: VariantSizeDifferences,
-            BoxPointers: BoxPointers,
             PathStatements: PathStatements,
             LetUnderscore: LetUnderscore,
             InvalidReferenceCasting: InvalidReferenceCasting,
@@ -551,6 +553,10 @@ fn register_builtins(store: &mut LintStore) {
         "converted into hard error, see RFC #3535 \
          <https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
     );
+    store.register_removed(
+        "box_pointers",
+        "it does not detect other kinds of allocations, and existed only for historical reasons",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 14084405d0e..54c73710eca 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -67,12 +67,6 @@ pub struct BuiltinWhileTrue {
 }
 
 #[derive(LintDiagnostic)]
-#[diag(lint_builtin_box_pointers)]
-pub struct BuiltinBoxPointers<'a> {
-    pub ty: Ty<'a>,
-}
-
-#[derive(LintDiagnostic)]
 #[diag(lint_builtin_non_shorthand_field_patterns)]
 pub struct BuiltinNonShorthandFieldPatterns {
     pub ident: Ident,
@@ -323,6 +317,13 @@ pub struct BuiltinTypeAliasGenericBounds<'a, 'b> {
     pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>,
 }
 
+#[derive(LintDiagnostic)]
+#[diag(lint_macro_expr_fragment_specifier_2024_migration)]
+pub struct MacroExprFragment2024 {
+    #[suggestion(code = "expr_2021", applicability = "machine-applicable")]
+    pub suggestion: Span,
+}
+
 pub struct BuiltinTypeAliasGenericBoundsSuggestion {
     pub suggestions: Vec<(Span, String)>,
 }
diff --git a/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
new file mode 100644
index 00000000000..867e132b106
--- /dev/null
+++ b/compiler/rustc_lint/src/macro_expr_fragment_specifier_2024_migration.rs
@@ -0,0 +1,155 @@
+//! Migration code for the `expr_fragment_specifier_2024`
+//! rule.
+use tracing::debug;
+
+use rustc_ast::token::Token;
+use rustc_ast::token::TokenKind;
+use rustc_ast::tokenstream::TokenStream;
+use rustc_ast::tokenstream::TokenTree;
+use rustc_session::declare_lint;
+use rustc_session::declare_lint_pass;
+use rustc_session::lint::FutureIncompatibilityReason;
+use rustc_span::edition::Edition;
+use rustc_span::sym;
+
+use crate::lints::MacroExprFragment2024;
+use crate::EarlyLintPass;
+
+declare_lint! {
+    /// The `edition_2024_expr_fragment_specifier` lint detects the use of
+    /// `expr` fragments in macros during migration to the 2024 edition.
+    ///
+    /// The `expr` fragment specifier will accept more expressions in the 2024
+    /// edition. To maintain the behavior from the 2021 edition and earlier, use
+    /// the `expr_2021` fragment specifier.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021,compile_fail
+    /// #![deny(edition_2024_expr_fragment_specifier)]
+    /// macro_rules! m {
+    ///   ($e:expr) => {
+    ///       $e
+    ///   }
+    /// }
+    ///
+    /// fn main() {
+    ///    m!(1);
+    /// }
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Rust [editions] allow the language to evolve without breaking backwards
+    /// compatibility. This lint catches code that uses [macro matcher fragment
+    /// specifiers] that have changed meaning in the 2024 edition. If you switch
+    /// to the new edition without updating the code, your macros may behave
+    /// differently.
+    ///
+    /// In the 2024 edition, the `expr` fragment specifier `expr` will also
+    /// match `const { ... }` blocks. This means if a macro had a pattern that
+    /// matched `$e:expr` and another that matches `const { $e: expr }`, for
+    /// example, that under the 2024 edition the first pattern would match while
+    /// in the 2021 and earlier editions the second pattern would match. To keep
+    /// the old behavior, use the `expr_2021` fragment specifier.
+    ///
+    /// This lint detects macros whose behavior might change due to the changing
+    /// meaning of the `expr` fragment specifier. It is "allow" by default
+    /// because the code is perfectly valid in older editions. The [`cargo fix`]
+    /// tool with the `--edition` flag will switch this lint to "warn" and
+    /// automatically apply the suggested fix from the compiler. This provides a
+    /// completely automated way to update old code for a new edition.
+    ///
+    /// Using `cargo fix --edition` with this lint will ensure that your code
+    /// retains the same behavior. This may not be the desired, as macro authors
+    /// often will want their macros to use the latest grammar for matching
+    /// expressions. Be sure to carefully review changes introduced by this lint
+    /// to ensure the macros implement the desired behavior.
+    ///
+    /// [editions]: https://doc.rust-lang.org/edition-guide/
+    /// [macro matcher fragment specifiers]: https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html
+    /// [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
+    pub EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
+    Allow,
+    "The `expr` fragment specifier will accept more expressions in the 2024 edition. \
+    To keep the existing behavior, use the `expr_2021` fragment specifier.",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
+        reference: "Migration Guide <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/macro-fragment-specifiers.html>",
+    };
+}
+
+declare_lint_pass!(Expr2024 => [EDITION_2024_EXPR_FRAGMENT_SPECIFIER,]);
+
+impl Expr2024 {
+    fn check_tokens(&mut self, cx: &crate::EarlyContext<'_>, tokens: &TokenStream) {
+        let mut prev_colon = false;
+        let mut prev_identifier = false;
+        let mut prev_dollar = false;
+        for tt in tokens.trees() {
+            debug!(
+                "check_tokens: {:?} - colon {prev_dollar} - ident {prev_identifier} - colon {prev_colon}",
+                tt
+            );
+            match tt {
+                TokenTree::Token(token, _) => match token.kind {
+                    TokenKind::Dollar => {
+                        prev_dollar = true;
+                        continue;
+                    }
+                    TokenKind::Ident(..) | TokenKind::NtIdent(..) => {
+                        if prev_colon && prev_identifier && prev_dollar {
+                            self.check_ident_token(cx, token);
+                        } else if prev_dollar {
+                            prev_identifier = true;
+                            continue;
+                        }
+                    }
+                    TokenKind::Colon => {
+                        if prev_dollar && prev_identifier {
+                            prev_colon = true;
+                            continue;
+                        }
+                    }
+                    _ => {}
+                },
+                TokenTree::Delimited(.., tts) => self.check_tokens(cx, tts),
+            }
+            prev_colon = false;
+            prev_identifier = false;
+            prev_dollar = false;
+        }
+    }
+
+    fn check_ident_token(&mut self, cx: &crate::EarlyContext<'_>, token: &Token) {
+        debug!("check_ident_token: {:?}", token);
+        let (sym, edition) = match token.kind {
+            TokenKind::Ident(sym, _) => (sym, Edition::Edition2024),
+            _ => return,
+        };
+
+        debug!("token.span.edition(): {:?}", token.span.edition());
+        if token.span.edition() >= edition {
+            return;
+        }
+
+        if sym != sym::expr {
+            return;
+        }
+
+        debug!("emitting lint");
+        cx.builder.emit_span_lint(
+            &EDITION_2024_EXPR_FRAGMENT_SPECIFIER,
+            token.span.into(),
+            MacroExprFragment2024 { suggestion: token.span },
+        );
+    }
+}
+
+impl EarlyLintPass for Expr2024 {
+    fn check_mac_def(&mut self, cx: &crate::EarlyContext<'_>, mc: &rustc_ast::MacroDef) {
+        self.check_tokens(cx, &mc.body.tokens);
+    }
+}
diff --git a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
index aa1d94228ea..445dcd41e5d 100644
--- a/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
+++ b/compiler/rustc_lint/src/multiple_supertrait_upcastable.rs
@@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for MultipleSupertraitUpcastable {
         {
             let direct_super_traits_iter = cx
                 .tcx
-                .super_predicates_of(def_id)
+                .explicit_super_predicates_of(def_id)
                 .predicates
                 .into_iter()
                 .filter_map(|(pred, _)| pred.as_trait_clause());
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 300dac442d5..2f8eea6cd18 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -11,10 +11,10 @@ use rustc_session::{declare_lint, impl_lint_pass};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::Span;
 use rustc_span::{sym, symbol::kw, ExpnKind, MacroKind, Symbol};
-use rustc_trait_selection::infer::TyCtxtInferExt;
-use rustc_trait_selection::traits::error_reporting::ambiguity::{
+use rustc_trait_selection::error_reporting::traits::ambiguity::{
     compute_applicable_impls_for_diagnostics, CandidateSource,
 };
+use rustc_trait_selection::infer::TyCtxtInferExt;
 
 use crate::fluent_generated as fluent;
 use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag};
@@ -50,7 +50,7 @@ declare_lint! {
     /// All nested bodies (functions, enum discriminant, array length, consts) (expect for
     /// `const _: Ty = { ... }` in top-level module, which is still undecided) are checked.
     pub NON_LOCAL_DEFINITIONS,
-    Warn,
+    Allow,
     "checks for non-local definitions",
     report_in_external_macro
 }
diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs
index 91441248e70..307e4bebe9a 100644
--- a/compiler/rustc_lint/src/noop_method_call.rs
+++ b/compiler/rustc_lint/src/noop_method_call.rs
@@ -96,7 +96,9 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall {
             .tcx
             .normalize_erasing_regions(cx.param_env, cx.typeck_results().node_args(expr.hir_id));
         // Resolve the trait method instance.
-        let Ok(Some(i)) = ty::Instance::resolve(cx.tcx, cx.param_env, did, args) else { return };
+        let Ok(Some(i)) = ty::Instance::try_resolve(cx.tcx, cx.param_env, did, args) else {
+            return;
+        };
         // (Re)check that it implements the noop diagnostic.
         let Some(name) = cx.tcx.get_diagnostic_name(i.def_id()) else { return };
         if !matches!(
diff --git a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
index 8b669bcc13f..5ee73dbfdc6 100644
--- a/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
+++ b/compiler/rustc_lint/src/opaque_hidden_inferred_bound.rs
@@ -76,9 +76,7 @@ impl<'tcx> LateLintPass<'tcx> for OpaqueHiddenInferredBound {
         // For every projection predicate in the opaque type's explicit bounds,
         // check that the type that we're assigning actually satisfies the bounds
         // of the associated type.
-        for (pred, pred_span) in
-            cx.tcx.explicit_item_bounds(def_id).instantiate_identity_iter_copied()
-        {
+        for (pred, pred_span) in cx.tcx.explicit_item_bounds(def_id).iter_identity_copied() {
             infcx.enter_forall(pred.kind(), |predicate| {
                 let ty::ClauseKind::Projection(proj) = predicate else {
                     return;
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 195a0f72475..65d42ed8054 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -298,9 +298,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
                 ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
                     elaborate(
                         cx.tcx,
-                        cx.tcx
-                            .explicit_item_super_predicates(def)
-                            .instantiate_identity_iter_copied(),
+                        cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(),
                     )
                     // We only care about self bounds for the impl-trait
                     .filter_only_self()
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 472e93d202d..48e20a586c6 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -74,11 +74,13 @@ declare_lint_pass! {
         NON_CONTIGUOUS_RANGE_ENDPOINTS,
         NON_EXHAUSTIVE_OMITTED_PATTERNS,
         ORDER_DEPENDENT_TRAIT_OBJECTS,
+        OUT_OF_SCOPE_MACRO_CALLS,
         OVERLAPPING_RANGE_ENDPOINTS,
         PATTERNS_IN_FNS_WITHOUT_BODY,
         PRIVATE_BOUNDS,
         PRIVATE_INTERFACES,
         PROC_MACRO_DERIVE_RESOLUTION_FALLBACK,
+        PTR_CAST_ADD_AUTO_TO_OBJECT,
         PUB_USE_OF_PRIVATE_EXTERN_CRATE,
         REDUNDANT_LIFETIMES,
         REFINING_IMPL_TRAIT_INTERNAL,
@@ -4188,6 +4190,7 @@ declare_lint! {
         reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange,
         reference: "issue #123748 <https://github.com/rust-lang/rust/issues/123748>",
     };
+    @edition Edition2024 => Deny;
     report_in_external_macro
 }
 
@@ -4617,7 +4620,7 @@ declare_lint! {
     /// [against]: https://github.com/rust-lang/rust/issues/38831
     /// [future-incompatible]: ../index.md#future-incompatible-lints
     pub ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
-    Warn,
+    Deny,
     "elided lifetimes cannot be used in associated constants in impls",
     @future_incompatible = FutureIncompatibleInfo {
         reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
@@ -4937,6 +4940,58 @@ declare_lint! {
 }
 
 declare_lint! {
+    /// The `ptr_cast_add_auto_to_object` lint detects casts of raw pointers to trait
+    /// objects, which add auto traits.
+    ///
+    /// ### Example
+    ///
+    /// ```rust,edition2021,compile_fail
+    /// let ptr: *const dyn core::any::Any = &();
+    /// _ = ptr as *const dyn core::any::Any + Send;
+    /// ```
+    ///
+    /// {{produces}}
+    ///
+    /// ### Explanation
+    ///
+    /// Adding an auto trait can make the vtable invalid, potentially causing
+    /// UB in safe code afterwards. For example:
+    ///
+    /// ```ignore (causes a warning)
+    /// #![feature(arbitrary_self_types)]
+    ///
+    /// trait Trait {
+    ///     fn f(self: *const Self)
+    ///     where
+    ///         Self: Send;
+    /// }
+    ///
+    /// impl Trait for *const () {
+    ///     fn f(self: *const Self) {
+    ///         unreachable!()
+    ///     }
+    /// }
+    ///
+    /// fn main() {
+    ///     let unsend: *const () = &();
+    ///     let unsend: *const dyn Trait = &unsend;
+    ///     let send_bad: *const (dyn Trait + Send) = unsend as _;
+    ///     send_bad.f(); // this crashes, since vtable for `*const ()` does not have an entry for `f`
+    /// }
+    /// ```
+    ///
+    /// Generally you must ensure that vtable is right for the pointer's type,
+    /// before passing the pointer to safe code.
+    pub PTR_CAST_ADD_AUTO_TO_OBJECT,
+    Warn,
+    "detects `as` casts from pointers to `dyn Trait` to pointers to `dyn Trait + Auto`",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
+        reference: "issue #127323 <https://github.com/rust-lang/rust/issues/127323>",
+    };
+}
+
+declare_lint! {
     /// The `out_of_scope_macro_calls` lint detects `macro_rules` called when they are not in scope,
     /// above their definition, which may happen in key-value attributes.
     ///
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index c4cfc0b6dc6..283c4fbbb7c 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -407,7 +407,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
     const char *ABIStr, LLVMRustCodeModel RustCM, LLVMRustRelocModel RustReloc,
     LLVMRustCodeGenOptLevel RustOptLevel, bool UseSoftFloat,
     bool FunctionSections, bool DataSections, bool UniqueSectionNames,
-    bool TrapUnreachable, bool Singlethread, bool AsmComments,
+    bool TrapUnreachable, bool Singlethread, bool VerboseAsm,
     bool EmitStackSizeSection, bool RelaxELFRelocations, bool UseInitArray,
     const char *SplitDwarfFile, const char *OutputObjFile,
     const char *DebugInfoCompression, bool UseEmulatedTls,
@@ -435,8 +435,9 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
   Options.DataSections = DataSections;
   Options.FunctionSections = FunctionSections;
   Options.UniqueSectionNames = UniqueSectionNames;
-  Options.MCOptions.AsmVerbose = AsmComments;
-  Options.MCOptions.PreserveAsmComments = AsmComments;
+  Options.MCOptions.AsmVerbose = VerboseAsm;
+  // Always preserve comments that were written by the user
+  Options.MCOptions.PreserveAsmComments = true;
   Options.MCOptions.ABIName = ABIStr;
   if (SplitDwarfFile) {
     Options.MCOptions.SplitDwarfFile = SplitDwarfFile;
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index ea7037740f1..83b41e0540e 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1348,7 +1348,9 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
     }
 
     fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
-        let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some() {
+        let name = if self.root.tables.opt_rpitit_info.get(self, id).is_some()
+            || self.root.tables.is_effects_desugaring.get(self, id)
+        {
             kw::Empty
         } else {
             self.item_name(id)
@@ -1371,6 +1373,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             container,
             fn_has_self_parameter: has_self,
             opt_rpitit_info,
+            is_effects_desugaring: self.root.tables.is_effects_desugaring.get(self, id),
         }
     }
 
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index c9450142cd3..6b240f0f0b3 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -211,8 +211,8 @@ provide! { tcx, def_id, other, cdata,
     explicit_predicates_of => { table }
     generics_of => { table }
     inferred_outlives_of => { table_defaulted_array }
-    super_predicates_of => { table }
-    implied_predicates_of => { table }
+    explicit_super_predicates_of => { table }
+    explicit_implied_predicates_of => { table }
     type_of => { table }
     type_alias_is_lazy => { cdata.root.tables.type_alias_is_lazy.get(cdata, def_id.index) }
     variances_of => { table }
@@ -279,6 +279,9 @@ provide! { tcx, def_id, other, cdata,
             .process_decoded(tcx, || panic!("{def_id:?} does not have trait_impl_trait_tys")))
     }
 
+    associated_type_for_effects => {
+        table
+    }
     associated_types_for_impl_traits_in_associated_fn => { table_defaulted_array }
 
     visibility => { cdata.get_visibility(def_id.index) }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 4bd2ec09a6e..209316ca20f 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1431,8 +1431,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             if let DefKind::Trait = def_kind {
                 record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
-                record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
-                record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id));
+                record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id));
+                record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id));
 
                 let module_children = self.tcx.module_children_local(local_id);
                 record_array!(self.tables.module_children_non_reexports[def_id] <-
@@ -1440,8 +1440,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             }
             if let DefKind::TraitAlias = def_kind {
                 record!(self.tables.trait_def[def_id] <- self.tcx.trait_def(def_id));
-                record!(self.tables.super_predicates_of[def_id] <- self.tcx.super_predicates_of(def_id));
-                record!(self.tables.implied_predicates_of[def_id] <- self.tcx.implied_predicates_of(def_id));
+                record!(self.tables.explicit_super_predicates_of[def_id] <- self.tcx.explicit_super_predicates_of(def_id));
+                record!(self.tables.explicit_implied_predicates_of[def_id] <- self.tcx.explicit_implied_predicates_of(def_id));
             }
             if let DefKind::Trait | DefKind::Impl { .. } = def_kind {
                 let associated_item_def_ids = self.tcx.associated_item_def_ids(def_id);
@@ -1454,6 +1454,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 for &def_id in associated_item_def_ids {
                     self.encode_info_for_assoc_item(def_id);
                 }
+                if let Some(assoc_def_id) = self.tcx.associated_type_for_effects(def_id) {
+                    record!(self.tables.associated_type_for_effects[def_id] <- assoc_def_id);
+                }
             }
             if def_kind == DefKind::Closure
                 && let Some(coroutine_kind) = self.tcx.coroutine_kind(def_id)
@@ -1634,6 +1637,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 );
             }
         }
+        if item.is_effects_desugaring {
+            self.tables.is_effects_desugaring.set(def_id.index, true);
+        }
     }
 
     fn encode_mir(&mut self) {
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index 87900c23d8d..2a44b3423ae 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -391,7 +391,9 @@ define_tables! {
     inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
     inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
     associated_types_for_impl_traits_in_associated_fn: Table<DefIndex, LazyArray<DefId>>,
+    associated_type_for_effects: Table<DefIndex, Option<LazyValue<DefId>>>,
     opt_rpitit_info: Table<DefIndex, Option<LazyValue<ty::ImplTraitInTraitData>>>,
+    is_effects_desugaring: Table<DefIndex, bool>,
     unused_generic_params: Table<DefIndex, UnusedGenericParams>,
     // Reexported names are not associated with individual `DefId`s,
     // e.g. a glob import can introduce a lot of names, all with the same `DefId`.
@@ -416,10 +418,10 @@ define_tables! {
     lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>,
     explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     generics_of: Table<DefIndex, LazyValue<ty::Generics>>,
-    super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
+    explicit_super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     // As an optimization, we only store this for trait aliases,
-    // since it's identical to super_predicates_of for traits.
-    implied_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
+    // since it's identical to explicit_super_predicates_of for traits.
+    explicit_implied_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
     type_of: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, Ty<'static>>>>,
     variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
     fn_sig: Table<DefIndex, LazyValue<ty::EarlyBinder<'static, ty::PolyFnSig<'static>>>>,
diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl
index f4d619329eb..2b9d9a07a98 100644
--- a/compiler/rustc_middle/messages.ftl
+++ b/compiler/rustc_middle/messages.ftl
@@ -41,6 +41,9 @@ middle_cannot_be_normalized =
 middle_conflict_types =
     this expression supplies two conflicting concrete types for the same opaque type
 
+middle_consider_type_length_limit =
+    consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate
+
 middle_const_eval_non_int =
     constant evaluation of enum discriminant resulted in non-integer
 
@@ -94,8 +97,11 @@ middle_strict_coherence_needs_negative_coherence =
     to use `strict_coherence` on this trait, the `with_negative_coherence` feature must be enabled
     .label = due to this attribute
 
+middle_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
+
 middle_unknown_layout =
     the type `{$ty}` has an unknown layout
 
 middle_values_too_big =
     values of the type `{$ty}` are too big for the current architecture
+middle_written_to_path = the full type name has been written to '{$path}'
diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs
index 6e622b0405f..711db4e0a6b 100644
--- a/compiler/rustc_middle/src/error.rs
+++ b/compiler/rustc_middle/src/error.rs
@@ -1,4 +1,5 @@
 use std::fmt;
+use std::path::PathBuf;
 
 use rustc_errors::{codes::*, DiagArgName, DiagArgValue, DiagMessage};
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -149,3 +150,16 @@ pub struct ErroneousConstant {
 
 /// Used by `rustc_const_eval`
 pub use crate::fluent_generated::middle_adjust_for_foreign_abi_error;
+
+#[derive(Diagnostic)]
+#[diag(middle_type_length_limit)]
+#[help(middle_consider_type_length_limit)]
+pub struct TypeLengthLimit {
+    #[primary_span]
+    pub span: Span,
+    pub shrunk: String,
+    #[note(middle_written_to_path)]
+    pub was_written: Option<()>,
+    pub path: PathBuf,
+    pub type_length: usize,
+}
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 305ba1ef3bb..2f3a6ee601b 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -31,9 +31,18 @@ pub struct Map<'hir> {
 
 /// An iterator that walks up the ancestor tree of a given `HirId`.
 /// Constructed using `tcx.hir().parent_iter(hir_id)`.
-pub struct ParentHirIterator<'hir> {
+struct ParentHirIterator<'hir> {
     current_id: HirId,
     map: Map<'hir>,
+    // Cache the current value of `hir_owner_nodes` to avoid repeatedly calling the same query for
+    // the same owner, which will uselessly record many times the same query dependency.
+    current_owner_nodes: Option<&'hir OwnerNodes<'hir>>,
+}
+
+impl<'hir> ParentHirIterator<'hir> {
+    fn new(map: Map<'hir>, current_id: HirId) -> ParentHirIterator<'hir> {
+        ParentHirIterator { current_id, map, current_owner_nodes: None }
+    }
 }
 
 impl<'hir> Iterator for ParentHirIterator<'hir> {
@@ -44,13 +53,22 @@ impl<'hir> Iterator for ParentHirIterator<'hir> {
             return None;
         }
 
-        // There are nodes that do not have entries, so we need to skip them.
-        let parent_id = self.map.tcx.parent_hir_id(self.current_id);
+        let HirId { owner, local_id } = self.current_id;
 
-        if parent_id == self.current_id {
-            self.current_id = CRATE_HIR_ID;
-            return None;
-        }
+        let parent_id = if local_id == ItemLocalId::ZERO {
+            // We go from an owner to its parent, so clear the cache.
+            self.current_owner_nodes = None;
+            self.map.tcx.hir_owner_parent(owner)
+        } else {
+            let owner_nodes =
+                self.current_owner_nodes.get_or_insert_with(|| self.map.tcx.hir_owner_nodes(owner));
+            let parent_local_id = owner_nodes.nodes[local_id].parent;
+            // HIR indexing should have checked that.
+            debug_assert_ne!(parent_local_id, local_id);
+            HirId { owner, local_id: parent_local_id }
+        };
+
+        debug_assert_ne!(parent_id, self.current_id);
 
         self.current_id = parent_id;
         return Some(parent_id);
@@ -479,7 +497,7 @@ impl<'hir> Map<'hir> {
     /// until the crate root is reached. Prefer this over your own loop using `parent_id`.
     #[inline]
     pub fn parent_id_iter(self, current_id: HirId) -> impl Iterator<Item = HirId> + 'hir {
-        ParentHirIterator { current_id, map: self }
+        ParentHirIterator::new(self, current_id)
     }
 
     /// Returns an iterator for the nodes in the ancestor tree of the `current_id`
diff --git a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
index c8f0d0795a3..3ddf889b63a 100644
--- a/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
+++ b/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs
@@ -45,6 +45,32 @@ pub struct CodegenFnAttrs {
     /// The `#[repr(align(...))]` attribute. Indicates the value of which the function should be
     /// aligned to.
     pub alignment: Option<Align>,
+    /// The `#[patchable_function_entry(...)]` attribute. Indicates how many nops should be around
+    /// the function entry.
+    pub patchable_function_entry: Option<PatchableFunctionEntry>,
+}
+
+#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
+pub struct PatchableFunctionEntry {
+    /// Nops to prepend to the function
+    prefix: u8,
+    /// Nops after entry, but before body
+    entry: u8,
+}
+
+impl PatchableFunctionEntry {
+    pub fn from_config(config: rustc_session::config::PatchableFunctionEntry) -> Self {
+        Self { prefix: config.prefix(), entry: config.entry() }
+    }
+    pub fn from_prefix_and_entry(prefix: u8, entry: u8) -> Self {
+        Self { prefix, entry }
+    }
+    pub fn prefix(&self) -> u8 {
+        self.prefix
+    }
+    pub fn entry(&self) -> u8 {
+        self.entry
+    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, TyEncodable, TyDecodable, HashStable)]
@@ -121,6 +147,7 @@ impl CodegenFnAttrs {
             no_sanitize: SanitizerSet::empty(),
             instruction_set: None,
             alignment: None,
+            patchable_function_entry: None,
         }
     }
 
diff --git a/compiler/rustc_middle/src/middle/dependency_format.rs b/compiler/rustc_middle/src/middle/dependency_format.rs
index e7d0cffc85c..a3aff9a1101 100644
--- a/compiler/rustc_middle/src/middle/dependency_format.rs
+++ b/compiler/rustc_middle/src/middle/dependency_format.rs
@@ -4,15 +4,15 @@
 //! For all the gory details, see the provider of the `dependency_formats`
 //! query.
 
+// FIXME: move this file to rustc_metadata::dependency_format, but
+// this will introduce circular dependency between rustc_metadata and rustc_middle
+
 use rustc_macros::{Decodable, Encodable, HashStable};
 use rustc_session::config::CrateType;
 
 /// A list of dependencies for a certain crate type.
 ///
 /// The length of this vector is the same as the number of external crates used.
-/// The value is None if the crate does not need to be linked (it was found
-/// statically in another dylib), or Some(kind) if it needs to be linked as
-/// `kind` (either static or dynamic).
 pub type DependencyList = Vec<Linkage>;
 
 /// A mapping of all required dependencies for a particular flavor of output.
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index e76d7af6e4a..a0c9af436e2 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -27,6 +27,10 @@ impl<'tcx> TyCtxt<'tcx> {
         self.lang_items().get(lang_item) == Some(def_id)
     }
 
+    pub fn as_lang_item(self, def_id: DefId) -> Option<LangItem> {
+        self.lang_items().from_def_id(def_id)
+    }
+
     /// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits,
     /// returns a corresponding [`ty::ClosureKind`].
     /// For any other [`DefId`] return `None`.
diff --git a/compiler/rustc_middle/src/middle/limits.rs b/compiler/rustc_middle/src/middle/limits.rs
index 4d698012749..d0b4f36a426 100644
--- a/compiler/rustc_middle/src/middle/limits.rs
+++ b/compiler/rustc_middle/src/middle/limits.rs
@@ -30,7 +30,7 @@ pub fn provide(providers: &mut Providers) {
             tcx.hir().krate_attrs(),
             tcx.sess,
             sym::type_length_limit,
-            1048576,
+            2usize.pow(24),
         ),
     }
 }
diff --git a/compiler/rustc_middle/src/mir/coverage.rs b/compiler/rustc_middle/src/mir/coverage.rs
index da25fbb0a82..beaaadd497d 100644
--- a/compiler/rustc_middle/src/mir/coverage.rs
+++ b/compiler/rustc_middle/src/mir/coverage.rs
@@ -103,7 +103,7 @@ pub enum CoverageKind {
     SpanMarker,
 
     /// Marks its enclosing basic block with an ID that can be referred to by
-    /// side data in [`BranchInfo`].
+    /// side data in [`CoverageInfoHi`].
     ///
     /// Should be erased before codegen (at some point after `InstrumentCoverage`).
     BlockMarker { id: BlockMarkerId },
@@ -274,10 +274,15 @@ pub struct FunctionCoverageInfo {
     pub mcdc_num_condition_bitmaps: usize,
 }
 
-/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.
+/// Coverage information for a function, recorded during MIR building and
+/// attached to the corresponding `mir::Body`. Used by the `InstrumentCoverage`
+/// MIR pass.
+///
+/// ("Hi" indicates that this is "high-level" information collected at the
+/// THIR/MIR boundary, before the MIR-based coverage instrumentation pass.)
 #[derive(Clone, Debug)]
 #[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
-pub struct BranchInfo {
+pub struct CoverageInfoHi {
     /// 1 more than the highest-numbered [`CoverageKind::BlockMarker`] that was
     /// injected into the MIR body. This makes it possible to allocate per-ID
     /// data structures without having to scan the entire body first.
diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs
index 16093cfca6a..4e95e600b5a 100644
--- a/compiler/rustc_middle/src/mir/interpret/mod.rs
+++ b/compiler/rustc_middle/src/mir/interpret/mod.rs
@@ -18,6 +18,7 @@ use smallvec::{smallvec, SmallVec};
 use tracing::{debug, trace};
 
 use rustc_ast::LitKind;
+use rustc_attr::InlineAttr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{HashMapExt, Lock};
 use rustc_errors::ErrorGuaranteed;
@@ -134,10 +135,11 @@ pub fn specialized_encode_alloc_id<'tcx, E: TyEncoder<I = TyCtxt<'tcx>>>(
             AllocDiscriminant::Alloc.encode(encoder);
             alloc.encode(encoder);
         }
-        GlobalAlloc::Function(fn_instance) => {
-            trace!("encoding {:?} with {:#?}", alloc_id, fn_instance);
+        GlobalAlloc::Function { instance, unique } => {
+            trace!("encoding {:?} with {:#?}", alloc_id, instance);
             AllocDiscriminant::Fn.encode(encoder);
-            fn_instance.encode(encoder);
+            instance.encode(encoder);
+            unique.encode(encoder);
         }
         GlobalAlloc::VTable(ty, poly_trait_ref) => {
             trace!("encoding {:?} with {ty:#?}, {poly_trait_ref:#?}", alloc_id);
@@ -285,7 +287,12 @@ impl<'s> AllocDecodingSession<'s> {
                     trace!("creating fn alloc ID");
                     let instance = ty::Instance::decode(decoder);
                     trace!("decoded fn alloc instance: {:?}", instance);
-                    let alloc_id = decoder.interner().reserve_and_set_fn_alloc(instance);
+                    let unique = bool::decode(decoder);
+                    // Here we cannot call `reserve_and_set_fn_alloc` as that would use a query, which
+                    // is not possible in this context. That's why the allocation stores
+                    // whether it is unique or not.
+                    let alloc_id =
+                        decoder.interner().reserve_and_set_fn_alloc_internal(instance, unique);
                     alloc_id
                 }
                 AllocDiscriminant::VTable => {
@@ -323,7 +330,12 @@ impl<'s> AllocDecodingSession<'s> {
 #[derive(Debug, Clone, Eq, PartialEq, Hash, TyDecodable, TyEncodable, HashStable)]
 pub enum GlobalAlloc<'tcx> {
     /// The alloc ID is used as a function pointer.
-    Function(Instance<'tcx>),
+    Function {
+        instance: Instance<'tcx>,
+        /// Stores whether this instance is unique, i.e. all pointers to this function use the same
+        /// alloc ID.
+        unique: bool,
+    },
     /// This alloc ID points to a symbolic (not-reified) vtable.
     VTable(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>),
     /// The alloc ID points to a "lazy" static variable that did not get computed (yet).
@@ -349,7 +361,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
     #[inline]
     pub fn unwrap_fn(&self) -> Instance<'tcx> {
         match *self {
-            GlobalAlloc::Function(instance) => instance,
+            GlobalAlloc::Function { instance, .. } => instance,
             _ => bug!("expected function, got {:?}", self),
         }
     }
@@ -368,7 +380,7 @@ impl<'tcx> GlobalAlloc<'tcx> {
     #[inline]
     pub fn address_space(&self, cx: &impl HasDataLayout) -> AddressSpace {
         match self {
-            GlobalAlloc::Function(..) => cx.data_layout().instruction_address_space,
+            GlobalAlloc::Function { .. } => cx.data_layout().instruction_address_space,
             GlobalAlloc::Static(..) | GlobalAlloc::Memory(..) | GlobalAlloc::VTable(..) => {
                 AddressSpace::DATA
             }
@@ -426,7 +438,7 @@ impl<'tcx> TyCtxt<'tcx> {
     fn reserve_and_set_dedup(self, alloc: GlobalAlloc<'tcx>) -> AllocId {
         let mut alloc_map = self.alloc_map.lock();
         match alloc {
-            GlobalAlloc::Function(..) | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {}
+            GlobalAlloc::Function { .. } | GlobalAlloc::Static(..) | GlobalAlloc::VTable(..) => {}
             GlobalAlloc::Memory(..) => bug!("Trying to dedup-reserve memory with real data!"),
         }
         if let Some(&alloc_id) = alloc_map.dedup.get(&alloc) {
@@ -445,30 +457,45 @@ impl<'tcx> TyCtxt<'tcx> {
         self.reserve_and_set_dedup(GlobalAlloc::Static(static_id))
     }
 
+    /// Generates an `AllocId` for a function. The caller must already have decided whether this
+    /// function obtains a unique AllocId or gets de-duplicated via the cache.
+    fn reserve_and_set_fn_alloc_internal(self, instance: Instance<'tcx>, unique: bool) -> AllocId {
+        let alloc = GlobalAlloc::Function { instance, unique };
+        if unique {
+            // Deduplicate.
+            self.reserve_and_set_dedup(alloc)
+        } else {
+            // Get a fresh ID.
+            let mut alloc_map = self.alloc_map.lock();
+            let id = alloc_map.reserve();
+            alloc_map.alloc_map.insert(id, alloc);
+            id
+        }
+    }
+
     /// Generates an `AllocId` for a function. Depending on the function type,
     /// this might get deduplicated or assigned a new ID each time.
     pub fn reserve_and_set_fn_alloc(self, instance: Instance<'tcx>) -> AllocId {
         // Functions cannot be identified by pointers, as asm-equal functions can get deduplicated
         // by the linker (we set the "unnamed_addr" attribute for LLVM) and functions can be
-        // duplicated across crates.
-        // We thus generate a new `AllocId` for every mention of a function. This means that
-        // `main as fn() == main as fn()` is false, while `let x = main as fn(); x == x` is true.
-        // However, formatting code relies on function identity (see #58320), so we only do
-        // this for generic functions. Lifetime parameters are ignored.
+        // duplicated across crates. We thus generate a new `AllocId` for every mention of a
+        // function. This means that `main as fn() == main as fn()` is false, while `let x = main as
+        // fn(); x == x` is true. However, as a quality-of-life feature it can be useful to identify
+        // certain functions uniquely, e.g. for backtraces. So we identify whether codegen will
+        // actually emit duplicate functions. It does that when they have non-lifetime generics, or
+        // when they can be inlined. All other functions are given a unique address.
+        // This is not a stable guarantee! The `inline` attribute is a hint and cannot be relied
+        // upon for anything. But if we don't do this, backtraces look terrible.
         let is_generic = instance
             .args
             .into_iter()
             .any(|kind| !matches!(kind.unpack(), GenericArgKind::Lifetime(_)));
-        if is_generic {
-            // Get a fresh ID.
-            let mut alloc_map = self.alloc_map.lock();
-            let id = alloc_map.reserve();
-            alloc_map.alloc_map.insert(id, GlobalAlloc::Function(instance));
-            id
-        } else {
-            // Deduplicate.
-            self.reserve_and_set_dedup(GlobalAlloc::Function(instance))
-        }
+        let can_be_inlined = match self.codegen_fn_attrs(instance.def_id()).inline {
+            InlineAttr::Never => false,
+            _ => true,
+        };
+        let unique = !is_generic && !can_be_inlined;
+        self.reserve_and_set_fn_alloc_internal(instance, unique)
     }
 
     /// Generates an `AllocId` for a (symbolic, not-reified) vtable. Will get deduplicated.
diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs
index 95857e8579d..96613592bbc 100644
--- a/compiler/rustc_middle/src/mir/interpret/queries.rs
+++ b/compiler/rustc_middle/src/mir/interpret/queries.rs
@@ -73,7 +73,7 @@ impl<'tcx> TyCtxt<'tcx> {
             bug!("did not expect inference variables here");
         }
 
-        match ty::Instance::resolve(
+        match ty::Instance::try_resolve(
             self, param_env,
             // FIXME: maybe have a separate version for resolving mir::UnevaluatedConst?
             ct.def, ct.args,
@@ -106,7 +106,7 @@ impl<'tcx> TyCtxt<'tcx> {
             bug!("did not expect inference variables here");
         }
 
-        match ty::Instance::resolve(self, param_env, ct.def, ct.args) {
+        match ty::Instance::try_resolve(self, param_env, ct.def, ct.args) {
             Ok(Some(instance)) => {
                 let cid = GlobalId { instance, promoted: None };
                 self.const_eval_global_id_for_typeck(param_env, cid, span).inspect(|_| {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index ef88b253864..83e3898cebf 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -430,11 +430,12 @@ pub struct Body<'tcx> {
 
     pub tainted_by_errors: Option<ErrorGuaranteed>,
 
-    /// Branch coverage information collected during MIR building, to be used by
-    /// the `InstrumentCoverage` pass.
+    /// Coverage information collected from THIR/MIR during MIR building,
+    /// to be used by the `InstrumentCoverage` pass.
     ///
-    /// Only present if branch coverage is enabled and this function is eligible.
-    pub coverage_branch_info: Option<Box<coverage::BranchInfo>>,
+    /// Only present if coverage is enabled and this function is eligible.
+    /// Boxed to limit space overhead in non-coverage builds.
+    pub coverage_info_hi: Option<Box<coverage::CoverageInfoHi>>,
 
     /// Per-function coverage information added by the `InstrumentCoverage`
     /// pass, to be used in conjunction with the coverage statements injected
@@ -484,7 +485,7 @@ impl<'tcx> Body<'tcx> {
             is_polymorphic: false,
             injection_phase: None,
             tainted_by_errors,
-            coverage_branch_info: None,
+            coverage_info_hi: None,
             function_coverage_info: None,
         };
         body.is_polymorphic = body.has_non_region_param();
@@ -515,7 +516,7 @@ impl<'tcx> Body<'tcx> {
             is_polymorphic: false,
             injection_phase: None,
             tainted_by_errors: None,
-            coverage_branch_info: None,
+            coverage_info_hi: None,
             function_coverage_info: None,
         };
         body.is_polymorphic = body.has_non_region_param();
diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs
index 4657f4dcf81..82625ae3d47 100644
--- a/compiler/rustc_middle/src/mir/pretty.rs
+++ b/compiler/rustc_middle/src/mir/pretty.rs
@@ -473,8 +473,8 @@ pub fn write_mir_intro<'tcx>(
     // Add an empty line before the first block is printed.
     writeln!(w)?;
 
-    if let Some(branch_info) = &body.coverage_branch_info {
-        write_coverage_branch_info(branch_info, w)?;
+    if let Some(coverage_info_hi) = &body.coverage_info_hi {
+        write_coverage_info_hi(coverage_info_hi, w)?;
     }
     if let Some(function_coverage_info) = &body.function_coverage_info {
         write_function_coverage_info(function_coverage_info, w)?;
@@ -483,18 +483,26 @@ pub fn write_mir_intro<'tcx>(
     Ok(())
 }
 
-fn write_coverage_branch_info(
-    branch_info: &coverage::BranchInfo,
+fn write_coverage_info_hi(
+    coverage_info_hi: &coverage::CoverageInfoHi,
     w: &mut dyn io::Write,
 ) -> io::Result<()> {
-    let coverage::BranchInfo { branch_spans, mcdc_branch_spans, mcdc_decision_spans, .. } =
-        branch_info;
+    let coverage::CoverageInfoHi {
+        num_block_markers: _,
+        branch_spans,
+        mcdc_branch_spans,
+        mcdc_decision_spans,
+    } = coverage_info_hi;
+
+    // Only add an extra trailing newline if we printed at least one thing.
+    let mut did_print = false;
 
     for coverage::BranchSpan { span, true_marker, false_marker } in branch_spans {
         writeln!(
             w,
             "{INDENT}coverage branch {{ true: {true_marker:?}, false: {false_marker:?} }} => {span:?}",
         )?;
+        did_print = true;
     }
 
     for coverage::MCDCBranchSpan {
@@ -510,6 +518,7 @@ fn write_coverage_branch_info(
             "{INDENT}coverage mcdc branch {{ condition_id: {:?}, true: {true_marker:?}, false: {false_marker:?}, depth: {decision_depth:?} }} => {span:?}",
             condition_info.map(|info| info.condition_id)
         )?;
+        did_print = true;
     }
 
     for coverage::MCDCDecisionSpan { span, num_conditions, end_markers, decision_depth } in
@@ -519,10 +528,10 @@ fn write_coverage_branch_info(
             w,
             "{INDENT}coverage mcdc decision {{ num_conditions: {num_conditions:?}, end: {end_markers:?}, depth: {decision_depth:?} }} => {span:?}"
         )?;
+        did_print = true;
     }
 
-    if !branch_spans.is_empty() || !mcdc_branch_spans.is_empty() || !mcdc_decision_spans.is_empty()
-    {
+    if did_print {
         writeln!(w)?;
     }
 
@@ -845,6 +854,16 @@ impl<'tcx> TerminatorKind<'tcx> {
                 }
                 write!(fmt, ")")
             }
+            TailCall { func, args, .. } => {
+                write!(fmt, "tailcall {func:?}(")?;
+                for (index, arg) in args.iter().enumerate() {
+                    if index > 0 {
+                        write!(fmt, ", ")?;
+                    }
+                    write!(fmt, "{:?}", arg)?;
+                }
+                write!(fmt, ")")
+            }
             Assert { cond, expected, msg, .. } => {
                 write!(fmt, "assert(")?;
                 if !expected {
@@ -912,7 +931,12 @@ impl<'tcx> TerminatorKind<'tcx> {
     pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
         use self::TerminatorKind::*;
         match *self {
-            Return | UnwindResume | UnwindTerminate(_) | Unreachable | CoroutineDrop => vec![],
+            Return
+            | TailCall { .. }
+            | UnwindResume
+            | UnwindTerminate(_)
+            | Unreachable
+            | CoroutineDrop => vec![],
             Goto { .. } => vec!["".into()],
             SwitchInt { ref targets, .. } => targets
                 .values
@@ -1449,7 +1473,7 @@ pub fn write_allocations<'tcx>(
             // This can't really happen unless there are bugs, but it doesn't cost us anything to
             // gracefully handle it and allow buggy rustc to be debugged via allocation printing.
             None => write!(w, " (deallocated)")?,
-            Some(GlobalAlloc::Function(inst)) => write!(w, " (fn: {inst})")?,
+            Some(GlobalAlloc::Function { instance, .. }) => write!(w, " (fn: {instance})")?,
             Some(GlobalAlloc::VTable(ty, Some(trait_ref))) => {
                 write!(w, " (vtable: impl {trait_ref} for {ty})")?
             }
diff --git a/compiler/rustc_middle/src/mir/query.rs b/compiler/rustc_middle/src/mir/query.rs
index 46b38e4a6a6..cd8e28522ec 100644
--- a/compiler/rustc_middle/src/mir/query.rs
+++ b/compiler/rustc_middle/src/mir/query.rs
@@ -274,6 +274,9 @@ pub enum ConstraintCategory<'tcx> {
 
     /// A constraint that doesn't correspond to anything the user sees.
     Internal,
+
+    /// An internal constraint derived from an illegal universe relation.
+    IllegalUniverse,
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs
index 2c2884f1897..620fa962d79 100644
--- a/compiler/rustc_middle/src/mir/syntax.rs
+++ b/compiler/rustc_middle/src/mir/syntax.rs
@@ -744,6 +744,36 @@ pub enum TerminatorKind<'tcx> {
         fn_span: Span,
     },
 
+    /// Tail call.
+    ///
+    /// Roughly speaking this is a chimera of [`Call`] and [`Return`], with some caveats.
+    /// Semantically tail calls consists of two actions:
+    /// - pop of the current stack frame
+    /// - a call to the `func`, with the return address of the **current** caller
+    ///   - so that a `return` inside `func` returns to the caller of the caller
+    ///     of the function that is currently being executed
+    ///
+    /// Note that in difference with [`Call`] this is missing
+    /// - `destination` (because it's always the return place)
+    /// - `target` (because it's always taken from the current stack frame)
+    /// - `unwind` (because it's always taken from the current stack frame)
+    ///
+    /// [`Call`]: TerminatorKind::Call
+    /// [`Return`]: TerminatorKind::Return
+    TailCall {
+        /// The function that’s being called.
+        func: Operand<'tcx>,
+        /// Arguments the function is called with.
+        /// These are owned by the callee, which is free to modify them.
+        /// This allows the memory occupied by "by-value" arguments to be
+        /// reused across function calls without duplicating the contents.
+        args: Box<[Spanned<Operand<'tcx>>]>,
+        // FIXME(explicit_tail_calls): should we have the span for `become`? is this span accurate? do we need it?
+        /// This `Span` is the span of the function, without the dot and receiver
+        /// (e.g. `foo(a, b)` in `x.foo(a, b)`
+        fn_span: Span,
+    },
+
     /// Evaluates the operand, which must have type `bool`. If it is not equal to `expected`,
     /// initiates a panic. Initiating a panic corresponds to a `Call` terminator with some
     /// unspecified constant as the function to call, all the operands stored in the `AssertMessage`
@@ -870,6 +900,7 @@ impl TerminatorKind<'_> {
             TerminatorKind::Unreachable => "Unreachable",
             TerminatorKind::Drop { .. } => "Drop",
             TerminatorKind::Call { .. } => "Call",
+            TerminatorKind::TailCall { .. } => "TailCall",
             TerminatorKind::Assert { .. } => "Assert",
             TerminatorKind::Yield { .. } => "Yield",
             TerminatorKind::CoroutineDrop => "CoroutineDrop",
diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs
index ed592612358..5b035d9579d 100644
--- a/compiler/rustc_middle/src/mir/terminator.rs
+++ b/compiler/rustc_middle/src/mir/terminator.rs
@@ -439,6 +439,7 @@ mod helper {
                 | CoroutineDrop
                 | Return
                 | Unreachable
+                | TailCall { .. }
                 | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None),
                 InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => {
                     targets.iter().copied().chain(Some(u))
@@ -479,6 +480,7 @@ mod helper {
                 | CoroutineDrop
                 | Return
                 | Unreachable
+                | TailCall { .. }
                 | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None),
                 InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => {
                     targets.iter_mut().chain(Some(u))
@@ -501,6 +503,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::CoroutineDrop
             | TerminatorKind::Yield { .. }
@@ -521,6 +524,7 @@ impl<'tcx> TerminatorKind<'tcx> {
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::CoroutineDrop
             | TerminatorKind::Yield { .. }
@@ -606,9 +610,12 @@ impl<'tcx> TerminatorKind<'tcx> {
     pub fn edges(&self) -> TerminatorEdges<'_, 'tcx> {
         use TerminatorKind::*;
         match *self {
-            Return | UnwindResume | UnwindTerminate(_) | CoroutineDrop | Unreachable => {
-                TerminatorEdges::None
-            }
+            Return
+            | TailCall { .. }
+            | UnwindResume
+            | UnwindTerminate(_)
+            | CoroutineDrop
+            | Unreachable => TerminatorEdges::None,
 
             Goto { target } => TerminatorEdges::Single(target),
 
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 7628a1ed2fe..0d3c419748b 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -540,6 +540,17 @@ macro_rules! make_mir_visitor {
                         );
                     }
 
+                    TerminatorKind::TailCall {
+                        func,
+                        args,
+                        fn_span: _,
+                    } => {
+                        self.visit_operand(func, location);
+                        for arg in args {
+                            self.visit_operand(&$($mutability)? arg.node, location);
+                        }
+                    },
+
                     TerminatorKind::Assert {
                         cond,
                         expected: _,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 230a44bcf24..33c27d41d86 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -368,6 +368,7 @@ rustc_queries! {
         desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// The set of item bounds (see [`TyCtxt::explicit_item_bounds`]) that
@@ -378,6 +379,7 @@ rustc_queries! {
         desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
+        feedable
     }
 
     /// Elaborated version of the predicates from `explicit_item_bounds`.
@@ -646,6 +648,9 @@ rustc_queries! {
     }
 
     /// Returns the predicates written explicitly by the user.
+    ///
+    /// You should probably use `predicates_of` unless you're looking for
+    /// predicates with explicit spans for diagnostics purposes.
     query explicit_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
         desc { |tcx| "computing explicit predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
@@ -662,29 +667,32 @@ rustc_queries! {
         feedable
     }
 
-    /// Maps from the `DefId` of a trait to the list of
-    /// super-predicates. This is a subset of the full list of
-    /// predicates. We store these in a separate map because we must
-    /// evaluate them even during type conversion, often before the
-    /// full predicates are available (note that supertraits have
-    /// additional acyclicity requirements).
-    query super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
+    /// Maps from the `DefId` of a trait to the list of super-predicates of the trait,
+    /// *before* elaboration (so it doesn't contain transitive super-predicates). This
+    /// is a subset of the full list of predicates. We store these in a separate map
+    /// because we must evaluate them even during type conversion, often before the full
+    /// predicates are available (note that super-predicates must not be cyclic).
+    query explicit_super_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
         desc { |tcx| "computing the super predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
     }
 
-    query implied_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
+    /// The predicates of the trait that are implied during elaboration. This is a
+    /// superset of the super-predicates of the trait, but a subset of the predicates
+    /// of the trait. For regular traits, this includes all super-predicates and their
+    /// associated type bounds. For trait aliases, currently, this includes all of the
+    /// predicates of the trait alias.
+    query explicit_implied_predicates_of(key: DefId) -> ty::GenericPredicates<'tcx> {
         desc { |tcx| "computing the implied predicates of `{}`", tcx.def_path_str(key) }
         cache_on_disk_if { key.is_local() }
         separate_provide_extern
     }
 
-    /// The `Option<Ident>` is the name of an associated type. If it is `None`, then this query
-    /// returns the full set of predicates. If `Some<Ident>`, then the query returns only the
-    /// subset of super-predicates that reference traits that define the given associated type.
-    /// This is used to avoid cycles in resolving types like `T::Item`.
-    query super_predicates_that_define_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
+    /// The Ident is the name of an associated type.The query returns only the subset
+    /// of supertraits that define the given associated type. This is used to avoid
+    /// cycles in resolving type-dependent associated item paths like `T::Item`.
+    query explicit_supertraits_containing_assoc_item(key: (DefId, rustc_span::symbol::Ident)) -> ty::GenericPredicates<'tcx> {
         desc { |tcx| "computing the super traits of `{}` with associated type name `{}`",
             tcx.def_path_str(key.0),
             key.1
@@ -847,6 +855,12 @@ rustc_queries! {
         separate_provide_extern
     }
 
+    query associated_type_for_effects(def_id: DefId) -> Option<DefId> {
+        desc { |tcx| "creating associated items for effects in `{}`", tcx.def_path_str(def_id) }
+        cache_on_disk_if { def_id.is_local() }
+        separate_provide_extern
+    }
+
     /// Given an impl trait in trait `opaque_ty_def_id`, create and return the corresponding
     /// associated item.
     query associated_type_for_impl_trait_in_trait(opaque_ty_def_id: LocalDefId) -> LocalDefId {
@@ -2183,8 +2197,8 @@ rustc_queries! {
     ///  * `Err(ErrorGuaranteed)` when the `Instance` resolution process
     ///    couldn't complete due to errors elsewhere - this is distinct
     ///    from `Ok(None)` to avoid misleading diagnostics when an error
-    ///    has already been/will be emitted, for the original cause
-    query resolve_instance(
+    ///    has already been/will be emitted, for the original cause.
+    query resolve_instance_raw(
         key: ty::ParamEnvAnd<'tcx, (DefId, GenericArgsRef<'tcx>)>
     ) -> Result<Option<ty::Instance<'tcx>>, ErrorGuaranteed> {
         desc { "resolving instance `{}`", ty::Instance::new(key.value.0, key.value.1) }
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index b4e3fae1b43..b74775142e4 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -7,7 +7,6 @@ pub mod select;
 pub mod solve;
 pub mod specialization_graph;
 mod structural_impls;
-pub mod util;
 
 use crate::mir::ConstraintCategory;
 use crate::ty::abstract_const::NotConstEvaluatable;
diff --git a/compiler/rustc_middle/src/traits/util.rs b/compiler/rustc_middle/src/traits/util.rs
deleted file mode 100644
index adbb6cf2ddc..00000000000
--- a/compiler/rustc_middle/src/traits/util.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use rustc_data_structures::fx::FxHashSet;
-
-use crate::ty::{Clause, PolyTraitRef, ToPolyTraitRef, TyCtxt, Upcast};
-
-/// Given a [`PolyTraitRef`], get the [`Clause`]s implied by the trait's definition.
-///
-/// This only exists in `rustc_middle` because the more powerful elaborator depends on
-/// `rustc_infer` for elaborating outlives bounds -- this should only be used for pretty
-/// printing.
-pub fn super_predicates_for_pretty_printing<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: PolyTraitRef<'tcx>,
-) -> impl Iterator<Item = Clause<'tcx>> {
-    let clause = trait_ref.upcast(tcx);
-    Elaborator { tcx, visited: FxHashSet::from_iter([clause]), stack: vec![clause] }
-}
-
-/// Like [`super_predicates_for_pretty_printing`], except it only returns traits and filters out
-/// all other [`Clause`]s.
-pub fn supertraits_for_pretty_printing<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: PolyTraitRef<'tcx>,
-) -> impl Iterator<Item = PolyTraitRef<'tcx>> {
-    super_predicates_for_pretty_printing(tcx, trait_ref).filter_map(|clause| {
-        clause.as_trait_clause().map(|trait_clause| trait_clause.to_poly_trait_ref())
-    })
-}
-
-struct Elaborator<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    visited: FxHashSet<Clause<'tcx>>,
-    stack: Vec<Clause<'tcx>>,
-}
-
-impl<'tcx> Elaborator<'tcx> {
-    fn elaborate(&mut self, trait_ref: PolyTraitRef<'tcx>) {
-        let super_predicates =
-            self.tcx.super_predicates_of(trait_ref.def_id()).predicates.iter().filter_map(
-                |&(pred, _)| {
-                    let clause = pred.instantiate_supertrait(self.tcx, trait_ref);
-                    self.visited.insert(clause).then_some(clause)
-                },
-            );
-
-        self.stack.extend(super_predicates);
-    }
-}
-
-impl<'tcx> Iterator for Elaborator<'tcx> {
-    type Item = Clause<'tcx>;
-
-    fn next(&mut self) -> Option<Clause<'tcx>> {
-        if let Some(clause) = self.stack.pop() {
-            if let Some(trait_clause) = clause.as_trait_clause() {
-                self.elaborate(trait_clause.to_poly_trait_ref());
-            }
-            Some(clause)
-        } else {
-            None
-        }
-    }
-}
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 8e221cdc603..88ee32eae95 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -229,6 +229,10 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     fn sized_constraint(self, tcx: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
         self.sized_constraint(tcx)
     }
+
+    fn is_fundamental(self) -> bool {
+        self.is_fundamental()
+    }
 }
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq, HashStable, TyEncodable, TyDecodable)]
diff --git a/compiler/rustc_middle/src/ty/assoc.rs b/compiler/rustc_middle/src/ty/assoc.rs
index 1cdde3f057c..820f5e950a9 100644
--- a/compiler/rustc_middle/src/ty/assoc.rs
+++ b/compiler/rustc_middle/src/ty/assoc.rs
@@ -34,6 +34,8 @@ pub struct AssocItem {
     /// return-position `impl Trait` in trait desugaring. The `ImplTraitInTraitData`
     /// provides additional information about its source.
     pub opt_rpitit_info: Option<ty::ImplTraitInTraitData>,
+
+    pub is_effects_desugaring: bool,
 }
 
 impl AssocItem {
diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs
index bade0d56415..bdd9a6bab2b 100644
--- a/compiler/rustc_middle/src/ty/closure.rs
+++ b/compiler/rustc_middle/src/ty/closure.rs
@@ -237,7 +237,7 @@ impl<'tcx> TyCtxt<'tcx> {
 /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
 ///        `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
 ///        Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
-///     2. Since we only look at the projections here function will return `bar.x` as an a valid
+///     2. Since we only look at the projections here function will return `bar.x` as a valid
 ///        ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
 ///        list are being applied to the same root variable.
 pub fn is_ancestor_or_same_capture(
diff --git a/compiler/rustc_middle/src/ty/consts/kind.rs b/compiler/rustc_middle/src/ty/consts/kind.rs
index bf834ef7607..98f35b6b8ab 100644
--- a/compiler/rustc_middle/src/ty/consts/kind.rs
+++ b/compiler/rustc_middle/src/ty/consts/kind.rs
@@ -54,6 +54,13 @@ pub struct Expr<'tcx> {
     pub kind: ExprKind,
     args: ty::GenericArgsRef<'tcx>,
 }
+
+impl<'tcx> rustc_type_ir::inherent::ExprConst<TyCtxt<'tcx>> for Expr<'tcx> {
+    fn args(self) -> ty::GenericArgsRef<'tcx> {
+        self.args
+    }
+}
+
 impl<'tcx> Expr<'tcx> {
     pub fn new_binop(
         tcx: TyCtxt<'tcx>,
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 4bac9396e59..aee42bfe3aa 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -37,7 +37,7 @@ use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use rustc_ast::{self as ast, attr};
 use rustc_data_structures::defer;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
@@ -92,6 +92,8 @@ use std::ops::{Bound, Deref};
 impl<'tcx> Interner for TyCtxt<'tcx> {
     type DefId = DefId;
     type LocalDefId = LocalDefId;
+    type Span = Span;
+
     type GenericArgs = ty::GenericArgsRef<'tcx>;
 
     type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
@@ -342,12 +344,19 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         )
     }
 
-    fn super_predicates_of(
+    fn explicit_super_predicates_of(
         self,
         def_id: DefId,
-    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> {
+        ty::EarlyBinder::bind(self.explicit_super_predicates_of(def_id).instantiate_identity(self))
+    }
+
+    fn explicit_implied_predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = (ty::Clause<'tcx>, Span)>> {
         ty::EarlyBinder::bind(
-            self.super_predicates_of(def_id).instantiate_identity(self).predicates.into_iter(),
+            self.explicit_implied_predicates_of(def_id).instantiate_identity(self),
         )
     }
 
@@ -363,6 +372,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
     }
 
+    fn as_lang_item(self, def_id: DefId) -> Option<TraitSolverLangItem> {
+        lang_item_to_trait_lang_item(self.lang_items().from_def_id(def_id)?)
+    }
+
     fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> {
         self.associated_items(def_id)
             .in_definition_order()
@@ -370,17 +383,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
             .map(|assoc_item| assoc_item.def_id)
     }
 
-    fn args_may_unify_deep(
-        self,
-        obligation_args: ty::GenericArgsRef<'tcx>,
-        impl_args: ty::GenericArgsRef<'tcx>,
-    ) -> bool {
-        ty::fast_reject::DeepRejectCtxt {
-            treat_obligation_params: ty::fast_reject::TreatParams::ForLookup,
-        }
-        .args_may_unify(obligation_args, impl_args)
-    }
-
     // This implementation is a bit different from `TyCtxt::for_each_relevant_impl`,
     // since we want to skip over blanket impls for non-rigid aliases, and also we
     // only want to consider types that *actually* unify with float/int vars.
@@ -526,20 +528,12 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.is_object_safe(trait_def_id)
     }
 
-    fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool {
-        self.trait_def(trait_def_id).implement_via_object
-    }
-
-    fn fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> {
-        self.fn_trait_kind_from_def_id(trait_def_id)
-    }
-
-    fn async_fn_trait_kind_from_def_id(self, trait_def_id: DefId) -> Option<ty::ClosureKind> {
-        self.async_fn_trait_kind_from_def_id(trait_def_id)
+    fn trait_is_fundamental(self, def_id: DefId) -> bool {
+        self.trait_def(def_id).is_fundamental
     }
 
-    fn supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator<Item = DefId> {
-        self.supertrait_def_ids(trait_def_id)
+    fn trait_may_be_implemented_via_object(self, trait_def_id: DefId) -> bool {
+        self.trait_def(trait_def_id).implement_via_object
     }
 
     fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed {
@@ -579,44 +573,83 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     ) -> Ty<'tcx> {
         placeholder.find_const_ty_from_env(param_env)
     }
+
+    fn anonymize_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        binder: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.anonymize_bound_vars(binder)
+    }
 }
 
-fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem {
-    match lang_item {
-        TraitSolverLangItem::AsyncDestruct => LangItem::AsyncDestruct,
-        TraitSolverLangItem::AsyncFnKindHelper => LangItem::AsyncFnKindHelper,
-        TraitSolverLangItem::AsyncFnKindUpvars => LangItem::AsyncFnKindUpvars,
-        TraitSolverLangItem::AsyncFnOnceOutput => LangItem::AsyncFnOnceOutput,
-        TraitSolverLangItem::AsyncIterator => LangItem::AsyncIterator,
-        TraitSolverLangItem::CallOnceFuture => LangItem::CallOnceFuture,
-        TraitSolverLangItem::CallRefFuture => LangItem::CallRefFuture,
-        TraitSolverLangItem::Clone => LangItem::Clone,
-        TraitSolverLangItem::Copy => LangItem::Copy,
-        TraitSolverLangItem::Coroutine => LangItem::Coroutine,
-        TraitSolverLangItem::CoroutineReturn => LangItem::CoroutineReturn,
-        TraitSolverLangItem::CoroutineYield => LangItem::CoroutineYield,
-        TraitSolverLangItem::Destruct => LangItem::Destruct,
-        TraitSolverLangItem::DiscriminantKind => LangItem::DiscriminantKind,
-        TraitSolverLangItem::DynMetadata => LangItem::DynMetadata,
-        TraitSolverLangItem::FnPtrTrait => LangItem::FnPtrTrait,
-        TraitSolverLangItem::FusedIterator => LangItem::FusedIterator,
-        TraitSolverLangItem::Future => LangItem::Future,
-        TraitSolverLangItem::FutureOutput => LangItem::FutureOutput,
-        TraitSolverLangItem::Iterator => LangItem::Iterator,
-        TraitSolverLangItem::Metadata => LangItem::Metadata,
-        TraitSolverLangItem::Option => LangItem::Option,
-        TraitSolverLangItem::PointeeTrait => LangItem::PointeeTrait,
-        TraitSolverLangItem::PointerLike => LangItem::PointerLike,
-        TraitSolverLangItem::Poll => LangItem::Poll,
-        TraitSolverLangItem::Sized => LangItem::Sized,
-        TraitSolverLangItem::TransmuteTrait => LangItem::TransmuteTrait,
-        TraitSolverLangItem::Tuple => LangItem::Tuple,
-        TraitSolverLangItem::Unpin => LangItem::Unpin,
-        TraitSolverLangItem::Unsize => LangItem::Unsize,
+macro_rules! bidirectional_lang_item_map {
+    ($($name:ident),+ $(,)?) => {
+        fn trait_lang_item_to_lang_item(lang_item: TraitSolverLangItem) -> LangItem {
+            match lang_item {
+                $(TraitSolverLangItem::$name => LangItem::$name,)+
+            }
+        }
+
+        fn lang_item_to_trait_lang_item(lang_item: LangItem) -> Option<TraitSolverLangItem> {
+            Some(match lang_item {
+                $(LangItem::$name => TraitSolverLangItem::$name,)+
+                _ => return None,
+            })
+        }
     }
 }
 
+bidirectional_lang_item_map! {
+// tidy-alphabetical-start
+    AsyncDestruct,
+    AsyncFn,
+    AsyncFnKindHelper,
+    AsyncFnKindUpvars,
+    AsyncFnMut,
+    AsyncFnOnce,
+    AsyncFnOnceOutput,
+    AsyncIterator,
+    CallOnceFuture,
+    CallRefFuture,
+    Clone,
+    Copy,
+    Coroutine,
+    CoroutineReturn,
+    CoroutineYield,
+    Destruct,
+    DiscriminantKind,
+    DynMetadata,
+    EffectsIntersection,
+    EffectsIntersectionOutput,
+    EffectsMaybe,
+    EffectsNoRuntime,
+    EffectsRuntime,
+    Fn,
+    FnMut,
+    FnOnce,
+    FnPtrTrait,
+    FusedIterator,
+    Future,
+    FutureOutput,
+    Iterator,
+    Metadata,
+    Option,
+    PointeeTrait,
+    PointerLike,
+    Poll,
+    Sized,
+    TransmuteTrait,
+    Tuple,
+    Unpin,
+    Unsize,
+// tidy-alphabetical-end
+}
+
 impl<'tcx> rustc_type_ir::inherent::DefId<TyCtxt<'tcx>> for DefId {
+    fn is_local(self) -> bool {
+        self.is_local()
+    }
+
     fn as_local(self) -> Option<LocalDefId> {
         self.as_local()
     }
@@ -2440,7 +2473,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Given the def_id of a Trait `trait_def_id` and the name of an associated item `assoc_name`
     /// returns true if the `trait_def_id` defines an associated item of name `assoc_name`.
     pub fn trait_may_define_assoc_item(self, trait_def_id: DefId, assoc_name: Ident) -> bool {
-        self.super_traits_of(trait_def_id).any(|trait_did| {
+        self.supertrait_def_ids(trait_def_id).any(|trait_did| {
             self.associated_items(trait_did)
                 .filter_by_name_unhygienic(assoc_name.name)
                 .any(|item| self.hygienic_eq(assoc_name, item.ident(self), trait_did))
@@ -2463,28 +2496,10 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally)
     /// does not compute the full elaborated super-predicates but just the set of def-ids. It is used
-    /// to identify which traits may define a given associated type to help avoid cycle errors.
-    /// Returns a `DefId` iterator.
-    fn super_traits_of(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
-        let mut set = FxHashSet::default();
-        let mut stack = vec![trait_def_id];
-
-        set.insert(trait_def_id);
-
-        iter::from_fn(move || -> Option<DefId> {
-            let trait_did = stack.pop()?;
-            let generic_predicates = self.super_predicates_of(trait_did);
-
-            for (predicate, _) in generic_predicates.predicates {
-                if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() {
-                    if set.insert(data.def_id()) {
-                        stack.push(data.def_id());
-                    }
-                }
-            }
-
-            Some(trait_did)
-        })
+    /// to identify which traits may define a given associated type to help avoid cycle errors,
+    /// and to make size estimates for vtable layout computation.
+    pub fn supertrait_def_ids(self, trait_def_id: DefId) -> impl Iterator<Item = DefId> + 'tcx {
+        rustc_type_ir::elaborate::supertrait_def_ids(self, trait_def_id)
     }
 
     /// Given a closure signature, returns an equivalent fn signature. Detuples
@@ -3095,9 +3110,9 @@ impl<'tcx> TyCtxt<'tcx> {
         matches!(
             node,
             hir::Node::Item(hir::Item {
-                kind: hir::ItemKind::Impl(hir::Impl { generics, .. }),
+                kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
                 ..
-            }) if generics.params.iter().any(|p| matches!(p.kind, hir::GenericParamKind::Const { is_host_effect: true, .. }))
+            }) if matches!(constness, hir::Constness::Const)
         )
     }
 
diff --git a/compiler/rustc_middle/src/ty/elaborate_impl.rs b/compiler/rustc_middle/src/ty/elaborate_impl.rs
new file mode 100644
index 00000000000..8c89a2d884b
--- /dev/null
+++ b/compiler/rustc_middle/src/ty/elaborate_impl.rs
@@ -0,0 +1,84 @@
+use rustc_span::Span;
+use rustc_type_ir::elaborate::Elaboratable;
+
+use crate::ty::{self, TyCtxt};
+
+impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Clause<'tcx> {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        self.as_predicate()
+    }
+
+    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+        clause
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        clause: ty::Clause<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        clause
+    }
+}
+
+impl<'tcx> Elaboratable<TyCtxt<'tcx>> for ty::Predicate<'tcx> {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        *self
+    }
+
+    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+        clause.as_predicate()
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        clause: ty::Clause<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        clause.as_predicate()
+    }
+}
+
+impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Predicate<'tcx>, Span) {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        self.0
+    }
+
+    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+        (clause.as_predicate(), self.1)
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        clause: ty::Clause<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        (clause.as_predicate(), self.1)
+    }
+}
+
+impl<'tcx> Elaboratable<TyCtxt<'tcx>> for (ty::Clause<'tcx>, Span) {
+    fn predicate(&self) -> ty::Predicate<'tcx> {
+        self.0.as_predicate()
+    }
+
+    fn child(&self, clause: ty::Clause<'tcx>) -> Self {
+        (clause, self.1)
+    }
+
+    fn child_with_derived_cause(
+        &self,
+        clause: ty::Clause<'tcx>,
+        _span: Span,
+        _parent_trait_pred: ty::PolyTraitPredicate<'tcx>,
+        _index: usize,
+    ) -> Self {
+        (clause, self.1)
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/fast_reject.rs b/compiler/rustc_middle/src/ty/fast_reject.rs
index 923667e609b..0413cfa5a63 100644
--- a/compiler/rustc_middle/src/ty/fast_reject.rs
+++ b/compiler/rustc_middle/src/ty/fast_reject.rs
@@ -1,369 +1,9 @@
-use crate::mir::Mutability;
-use crate::ty::GenericArgKind;
-use crate::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt};
 use rustc_hir::def_id::DefId;
-use rustc_macros::{HashStable, TyDecodable, TyEncodable};
-use std::fmt::Debug;
-use std::hash::Hash;
-use std::iter;
 
-/// See `simplify_type`.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)]
-pub enum SimplifiedType {
-    Bool,
-    Char,
-    Int(ty::IntTy),
-    Uint(ty::UintTy),
-    Float(ty::FloatTy),
-    Adt(DefId),
-    Foreign(DefId),
-    Str,
-    Array,
-    Slice,
-    Ref(Mutability),
-    Ptr(Mutability),
-    Never,
-    Tuple(usize),
-    /// A trait object, all of whose components are markers
-    /// (e.g., `dyn Send + Sync`).
-    MarkerTraitObject,
-    Trait(DefId),
-    Closure(DefId),
-    Coroutine(DefId),
-    CoroutineWitness(DefId),
-    Function(usize),
-    Placeholder,
-    Error,
-}
+use super::TyCtxt;
 
-/// Generic parameters are pretty much just bound variables, e.g.
-/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as
-/// `for<'a, T> fn(&'a T) -> u32`.
-///
-/// Typecheck of `foo` has to succeed for all possible generic arguments, so
-/// during typeck, we have to treat its generic parameters as if they
-/// were placeholders.
-///
-/// But when calling `foo` we only have to provide a specific generic argument.
-/// In that case the generic parameters are instantiated with inference variables.
-/// As we use `simplify_type` before that instantiation happens, we just treat
-/// generic parameters as if they were inference variables in that case.
-#[derive(PartialEq, Eq, Debug, Clone, Copy)]
-pub enum TreatParams {
-    /// Treat parameters as infer vars. This is the correct mode for caching
-    /// an impl's type for lookup.
-    AsCandidateKey,
-    /// Treat parameters as placeholders in the given environment. This is the
-    /// correct mode for *lookup*, as during candidate selection.
-    ///
-    /// This also treats projections with inference variables as infer vars
-    /// since they could be further normalized.
-    ForLookup,
-}
+pub use rustc_type_ir::fast_reject::*;
 
-/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists.
-///
-/// **This function should only be used if you need to store or retrieve the type from some
-/// hashmap. If you want to quickly decide whether two types may unify, use the [DeepRejectCtxt]
-/// instead.**
-///
-/// The idea is to get something simple that we can use to quickly decide if two types could unify,
-/// for example during method lookup. If this function returns `Some(x)` it can only unify with
-/// types for which this method returns either `Some(x)` as well or `None`.
-///
-/// A special case here are parameters and projections, which are only injective
-/// if they are treated as placeholders.
-///
-/// For example when storing impls based on their simplified self type, we treat
-/// generic parameters as if they were inference variables. We must not simplify them here,
-/// as they can unify with any other type.
-///
-/// With projections we have to be even more careful, as treating them as placeholders
-/// is only correct if they are fully normalized.
-///
-/// ¹ meaning that if the outermost layers are different, then the whole types are also different.
-pub fn simplify_type<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    ty: Ty<'tcx>,
-    treat_params: TreatParams,
-) -> Option<SimplifiedType> {
-    match *ty.kind() {
-        ty::Bool => Some(SimplifiedType::Bool),
-        ty::Char => Some(SimplifiedType::Char),
-        ty::Int(int_type) => Some(SimplifiedType::Int(int_type)),
-        ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)),
-        ty::Float(float_type) => Some(SimplifiedType::Float(float_type)),
-        ty::Adt(def, _) => Some(SimplifiedType::Adt(def.did())),
-        ty::Str => Some(SimplifiedType::Str),
-        ty::Array(..) => Some(SimplifiedType::Array),
-        ty::Slice(..) => Some(SimplifiedType::Slice),
-        ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params),
-        ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)),
-        ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() {
-            Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => {
-                Some(SimplifiedType::Trait(principal_def_id))
-            }
-            _ => Some(SimplifiedType::MarkerTraitObject),
-        },
-        ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
-        ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => {
-            Some(SimplifiedType::Closure(def_id))
-        }
-        ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)),
-        ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)),
-        ty::Never => Some(SimplifiedType::Never),
-        ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())),
-        ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())),
-        ty::Placeholder(..) => Some(SimplifiedType::Placeholder),
-        ty::Param(_) => match treat_params {
-            TreatParams::ForLookup => Some(SimplifiedType::Placeholder),
-            TreatParams::AsCandidateKey => None,
-        },
-        ty::Alias(..) => match treat_params {
-            // When treating `ty::Param` as a placeholder, projections also
-            // don't unify with anything else as long as they are fully normalized.
-            // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder`
-            // when the new solver is enabled by default.
-            TreatParams::ForLookup if !ty.has_non_region_infer() => {
-                Some(SimplifiedType::Placeholder)
-            }
-            TreatParams::ForLookup | TreatParams::AsCandidateKey => None,
-        },
-        ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)),
-        ty::Error(_) => Some(SimplifiedType::Error),
-        ty::Bound(..) | ty::Infer(_) => None,
-    }
-}
+pub type DeepRejectCtxt<'tcx> = rustc_type_ir::fast_reject::DeepRejectCtxt<TyCtxt<'tcx>>;
 
-impl SimplifiedType {
-    pub fn def(self) -> Option<DefId> {
-        match self {
-            SimplifiedType::Adt(d)
-            | SimplifiedType::Foreign(d)
-            | SimplifiedType::Trait(d)
-            | SimplifiedType::Closure(d)
-            | SimplifiedType::Coroutine(d)
-            | SimplifiedType::CoroutineWitness(d) => Some(d),
-            _ => None,
-        }
-    }
-}
-
-/// Given generic arguments from an obligation and an impl,
-/// could these two be unified after replacing parameters in the
-/// the impl with inference variables.
-///
-/// For obligations, parameters won't be replaced by inference
-/// variables and only unify with themselves. We treat them
-/// the same way we treat placeholders.
-///
-/// We also use this function during coherence. For coherence the
-/// impls only have to overlap for some value, so we treat parameters
-/// on both sides like inference variables. This behavior is toggled
-/// using the `treat_obligation_params` field.
-#[derive(Debug, Clone, Copy)]
-pub struct DeepRejectCtxt {
-    pub treat_obligation_params: TreatParams,
-}
-
-impl DeepRejectCtxt {
-    pub fn args_may_unify<'tcx>(
-        self,
-        obligation_args: GenericArgsRef<'tcx>,
-        impl_args: GenericArgsRef<'tcx>,
-    ) -> bool {
-        iter::zip(obligation_args, impl_args).all(|(obl, imp)| {
-            match (obl.unpack(), imp.unpack()) {
-                // We don't fast reject based on regions.
-                (GenericArgKind::Lifetime(_), GenericArgKind::Lifetime(_)) => true,
-                (GenericArgKind::Type(obl), GenericArgKind::Type(imp)) => {
-                    self.types_may_unify(obl, imp)
-                }
-                (GenericArgKind::Const(obl), GenericArgKind::Const(imp)) => {
-                    self.consts_may_unify(obl, imp)
-                }
-                _ => bug!("kind mismatch: {obl} {imp}"),
-            }
-        })
-    }
-
-    pub fn types_may_unify<'tcx>(self, obligation_ty: Ty<'tcx>, impl_ty: Ty<'tcx>) -> bool {
-        match impl_ty.kind() {
-            // Start by checking whether the type in the impl may unify with
-            // pretty much everything. Just return `true` in that case.
-            ty::Param(_) | ty::Error(_) | ty::Alias(..) => return true,
-            // These types only unify with inference variables or their own
-            // variant.
-            ty::Bool
-            | ty::Char
-            | ty::Int(_)
-            | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Adt(..)
-            | ty::Str
-            | ty::Array(..)
-            | ty::Slice(..)
-            | ty::RawPtr(..)
-            | ty::Dynamic(..)
-            | ty::Pat(..)
-            | ty::Ref(..)
-            | ty::Never
-            | ty::Tuple(..)
-            | ty::FnPtr(..)
-            | ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()),
-            ty::FnDef(..)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | ty::Coroutine(..)
-            | ty::CoroutineWitness(..)
-            | ty::Placeholder(..)
-            | ty::Bound(..)
-            | ty::Infer(_) => bug!("unexpected impl_ty: {impl_ty}"),
-        }
-
-        let k = impl_ty.kind();
-        match *obligation_ty.kind() {
-            // Purely rigid types, use structural equivalence.
-            ty::Bool
-            | ty::Char
-            | ty::Int(_)
-            | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Str
-            | ty::Never
-            | ty::Foreign(_) => obligation_ty == impl_ty,
-            ty::Ref(_, obl_ty, obl_mutbl) => match k {
-                &ty::Ref(_, impl_ty, impl_mutbl) => {
-                    obl_mutbl == impl_mutbl && self.types_may_unify(obl_ty, impl_ty)
-                }
-                _ => false,
-            },
-            ty::Adt(obl_def, obl_args) => match k {
-                &ty::Adt(impl_def, impl_args) => {
-                    obl_def == impl_def && self.args_may_unify(obl_args, impl_args)
-                }
-                _ => false,
-            },
-            ty::Pat(obl_ty, _) => {
-                // FIXME(pattern_types): take pattern into account
-                matches!(k, &ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty))
-            }
-            ty::Slice(obl_ty) => {
-                matches!(k, &ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty))
-            }
-            ty::Array(obl_ty, obl_len) => match k {
-                &ty::Array(impl_ty, impl_len) => {
-                    self.types_may_unify(obl_ty, impl_ty)
-                        && self.consts_may_unify(obl_len, impl_len)
-                }
-                _ => false,
-            },
-            ty::Tuple(obl) => match k {
-                &ty::Tuple(imp) => {
-                    obl.len() == imp.len()
-                        && iter::zip(obl, imp).all(|(obl, imp)| self.types_may_unify(obl, imp))
-                }
-                _ => false,
-            },
-            ty::RawPtr(obl_ty, obl_mutbl) => match *k {
-                ty::RawPtr(imp_ty, imp_mutbl) => {
-                    obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty)
-                }
-                _ => false,
-            },
-            ty::Dynamic(obl_preds, ..) => {
-                // Ideally we would walk the existential predicates here or at least
-                // compare their length. But considering that the relevant `Relate` impl
-                // actually sorts and deduplicates these, that doesn't work.
-                matches!(k, ty::Dynamic(impl_preds, ..) if
-                    obl_preds.principal_def_id() == impl_preds.principal_def_id()
-                )
-            }
-            ty::FnPtr(obl_sig) => match k {
-                ty::FnPtr(impl_sig) => {
-                    let ty::FnSig { inputs_and_output, c_variadic, safety, abi } =
-                        obl_sig.skip_binder();
-                    let impl_sig = impl_sig.skip_binder();
-
-                    abi == impl_sig.abi
-                        && c_variadic == impl_sig.c_variadic
-                        && safety == impl_sig.safety
-                        && inputs_and_output.len() == impl_sig.inputs_and_output.len()
-                        && iter::zip(inputs_and_output, impl_sig.inputs_and_output)
-                            .all(|(obl, imp)| self.types_may_unify(obl, imp))
-                }
-                _ => false,
-            },
-
-            // Impls cannot contain these types as these cannot be named directly.
-            ty::FnDef(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => false,
-
-            // Placeholder types don't unify with anything on their own
-            ty::Placeholder(..) | ty::Bound(..) => false,
-
-            // Depending on the value of `treat_obligation_params`, we either
-            // treat generic parameters like placeholders or like inference variables.
-            ty::Param(_) => match self.treat_obligation_params {
-                TreatParams::ForLookup => false,
-                TreatParams::AsCandidateKey => true,
-            },
-
-            ty::Infer(ty::IntVar(_)) => impl_ty.is_integral(),
-
-            ty::Infer(ty::FloatVar(_)) => impl_ty.is_floating_point(),
-
-            ty::Infer(_) => true,
-
-            // As we're walking the whole type, it may encounter projections
-            // inside of binders and what not, so we're just going to assume that
-            // projections can unify with other stuff.
-            //
-            // Looking forward to lazy normalization this is the safer strategy anyways.
-            ty::Alias(..) => true,
-
-            ty::Error(_) => true,
-
-            ty::CoroutineWitness(..) => {
-                bug!("unexpected obligation type: {:?}", obligation_ty)
-            }
-        }
-    }
-
-    pub fn consts_may_unify(self, obligation_ct: ty::Const<'_>, impl_ct: ty::Const<'_>) -> bool {
-        let impl_val = match impl_ct.kind() {
-            ty::ConstKind::Expr(_)
-            | ty::ConstKind::Param(_)
-            | ty::ConstKind::Unevaluated(_)
-            | ty::ConstKind::Error(_) => {
-                return true;
-            }
-            ty::ConstKind::Value(_, impl_val) => impl_val,
-            ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
-                bug!("unexpected impl arg: {:?}", impl_ct)
-            }
-        };
-
-        match obligation_ct.kind() {
-            ty::ConstKind::Param(_) => match self.treat_obligation_params {
-                TreatParams::ForLookup => false,
-                TreatParams::AsCandidateKey => true,
-            },
-
-            // Placeholder consts don't unify with anything on their own
-            ty::ConstKind::Placeholder(_) => false,
-
-            // As we don't necessarily eagerly evaluate constants,
-            // they might unify with any value.
-            ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
-                true
-            }
-            ty::ConstKind::Value(_, obl_val) => obl_val == impl_val,
-
-            ty::ConstKind::Infer(_) => true,
-
-            ty::ConstKind::Bound(..) => {
-                bug!("unexpected obl const: {:?}", obligation_ct)
-            }
-        }
-    }
-}
+pub type SimplifiedType = rustc_type_ir::fast_reject::SimplifiedType<DefId>;
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 6467689a8aa..11ed0bdaa70 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -14,7 +14,7 @@ use super::{Clause, InstantiatedPredicates, ParamConst, ParamTy, Ty, TyCtxt};
 pub enum GenericParamDefKind {
     Lifetime,
     Type { has_default: bool, synthetic: bool },
-    Const { has_default: bool, is_host_effect: bool },
+    Const { has_default: bool, is_host_effect: bool, synthetic: bool },
 }
 
 impl GenericParamDefKind {
@@ -371,6 +371,7 @@ impl<'tcx> Generics {
 pub struct GenericPredicates<'tcx> {
     pub parent: Option<DefId>,
     pub predicates: &'tcx [(Clause<'tcx>, Span)],
+    pub effects_min_tys: &'tcx ty::List<Ty<'tcx>>,
 }
 
 impl<'tcx> GenericPredicates<'tcx> {
@@ -393,7 +394,7 @@ impl<'tcx> GenericPredicates<'tcx> {
     }
 
     pub fn instantiate_own_identity(&self) -> impl Iterator<Item = (Clause<'tcx>, Span)> {
-        EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied()
+        EarlyBinder::bind(self.predicates).iter_identity_copied()
     }
 
     #[instrument(level = "debug", skip(self, tcx))]
diff --git a/compiler/rustc_middle/src/ty/impls_ty.rs b/compiler/rustc_middle/src/ty/impls_ty.rs
index efcf428c213..9be7370a1c2 100644
--- a/compiler/rustc_middle/src/ty/impls_ty.rs
+++ b/compiler/rustc_middle/src/ty/impls_ty.rs
@@ -4,7 +4,6 @@
 use crate::middle::region;
 use crate::mir;
 use crate::ty;
-use crate::ty::fast_reject::SimplifiedType;
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::HashingControls;
@@ -57,18 +56,6 @@ where
     }
 }
 
-impl<'a> ToStableHashKey<StableHashingContext<'a>> for SimplifiedType {
-    type KeyType = Fingerprint;
-
-    #[inline]
-    fn to_stable_hash_key(&self, hcx: &StableHashingContext<'a>) -> Fingerprint {
-        let mut hasher = StableHasher::new();
-        let mut hcx: StableHashingContext<'a> = hcx.clone();
-        self.hash_stable(&mut hcx, &mut hasher);
-        hasher.finish()
-    }
-}
-
 impl<'a, 'tcx> HashStable<StableHashingContext<'a>> for ty::GenericArg<'tcx> {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         self.unpack().hash_stable(hcx, hasher);
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 1ba8820e0e1..c50a98e88fd 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -1,23 +1,26 @@
+use crate::error;
 use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use crate::ty::print::{FmtPrinter, Printer};
-use crate::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable};
-use crate::ty::{EarlyBinder, GenericArgs, GenericArgsRef, TypeVisitableExt};
+use crate::ty::print::{shrunk_instance_name, FmtPrinter, Printer};
+use crate::ty::{
+    self, EarlyBinder, GenericArgs, GenericArgsRef, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
+    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{CrateNum, DefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_index::bit_set::FiniteBitSet;
-use rustc_macros::{
-    Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable, TypeVisitable,
-};
+use rustc_macros::{Decodable, Encodable, HashStable, Lift, TyDecodable, TyEncodable};
 use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
 use rustc_span::def_id::LOCAL_CRATE;
-use rustc_span::Symbol;
+use rustc_span::{Span, Symbol, DUMMY_SP};
 use tracing::{debug, instrument};
 
 use std::assert_matches::assert_matches;
 use std::fmt;
+use std::path::PathBuf;
 
 /// An `InstanceKind` along with the args that are needed to substitute the instance.
 ///
@@ -385,7 +388,40 @@ impl<'tcx> InstanceKind<'tcx> {
     }
 }
 
-fn fmt_instance(
+fn type_length<'tcx>(item: impl TypeVisitable<TyCtxt<'tcx>>) -> usize {
+    struct Visitor<'tcx> {
+        type_length: usize,
+        cache: FxHashMap<Ty<'tcx>, usize>,
+    }
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Visitor<'tcx> {
+        fn visit_ty(&mut self, t: Ty<'tcx>) {
+            if let Some(&value) = self.cache.get(&t) {
+                self.type_length += value;
+                return;
+            }
+
+            let prev = self.type_length;
+            self.type_length += 1;
+            t.super_visit_with(self);
+
+            // We don't try to use the cache if the type is fairly small.
+            if self.type_length > 16 {
+                self.cache.insert(t, self.type_length - prev);
+            }
+        }
+
+        fn visit_const(&mut self, ct: ty::Const<'tcx>) {
+            self.type_length += 1;
+            ct.super_visit_with(self);
+        }
+    }
+    let mut visitor = Visitor { type_length: 0, cache: Default::default() };
+    item.visit_with(&mut visitor);
+
+    visitor.type_length
+}
+
+pub fn fmt_instance(
     f: &mut fmt::Formatter<'_>,
     instance: Instance<'_>,
     type_length: Option<rustc_session::Limit>,
@@ -485,19 +521,30 @@ impl<'tcx> Instance<'tcx> {
     ///
     /// Presuming that coherence and type-check have succeeded, if this method is invoked
     /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return
-    /// `Ok(Some(instance))`.
+    /// `Ok(Some(instance))`, **except** for when the instance's inputs hit the type size limit,
+    /// in which case it may bail out and return `Ok(None)`.
     ///
     /// Returns `Err(ErrorGuaranteed)` when the `Instance` resolution process
     /// couldn't complete due to errors elsewhere - this is distinct
     /// from `Ok(None)` to avoid misleading diagnostics when an error
     /// has already been/will be emitted, for the original cause
     #[instrument(level = "debug", skip(tcx), ret)]
-    pub fn resolve(
+    pub fn try_resolve(
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
     ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+        // Rust code can easily create exponentially-long types using only a
+        // polynomial recursion depth. Even with the default recursion
+        // depth, you can easily get cases that take >2^60 steps to run,
+        // which means that rustc basically hangs.
+        //
+        // Bail out in these cases to avoid that bad user experience.
+        if !tcx.type_length_limit().value_within_limit(type_length(args)) {
+            return Ok(None);
+        }
+
         // All regions in the result of this query are erased, so it's
         // fine to erase all of the input regions.
 
@@ -505,7 +552,7 @@ impl<'tcx> Instance<'tcx> {
         // below is more likely to ignore the bounds in scope (e.g. if the only
         // generic parameters mentioned by `args` were lifetime ones).
         let args = tcx.erase_regions(args);
-        tcx.resolve_instance(tcx.erase_regions(param_env.and((def_id, args))))
+        tcx.resolve_instance_raw(tcx.erase_regions(param_env.and((def_id, args))))
     }
 
     pub fn expect_resolve(
@@ -513,10 +560,48 @@ impl<'tcx> Instance<'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
+        span: Span,
     ) -> Instance<'tcx> {
-        match ty::Instance::resolve(tcx, param_env, def_id, args) {
+        // We compute the span lazily, to avoid unnecessary query calls.
+        // If `span` is a DUMMY_SP, and the def id is local, then use the
+        // def span of the def id.
+        let span_or_local_def_span =
+            || if span.is_dummy() && def_id.is_local() { tcx.def_span(def_id) } else { span };
+
+        match ty::Instance::try_resolve(tcx, param_env, def_id, args) {
             Ok(Some(instance)) => instance,
-            instance => bug!(
+            Ok(None) => {
+                let type_length = type_length(args);
+                if !tcx.type_length_limit().value_within_limit(type_length) {
+                    let (shrunk, written_to_path) =
+                        shrunk_instance_name(tcx, Instance::new(def_id, args));
+                    let mut path = PathBuf::new();
+                    let was_written = if let Some(path2) = written_to_path {
+                        path = path2;
+                        Some(())
+                    } else {
+                        None
+                    };
+                    tcx.dcx().emit_fatal(error::TypeLengthLimit {
+                        // We don't use `def_span(def_id)` so that diagnostics point
+                        // to the crate root during mono instead of to foreign items.
+                        // This is arguably better.
+                        span: span_or_local_def_span(),
+                        shrunk,
+                        was_written,
+                        path,
+                        type_length,
+                    });
+                } else {
+                    span_bug!(
+                        span_or_local_def_span(),
+                        "failed to resolve instance for {}",
+                        tcx.def_path_str_with_args(def_id, args)
+                    )
+                }
+            }
+            instance => span_bug!(
+                span_or_local_def_span(),
                 "failed to resolve instance for {}: {instance:#?}",
                 tcx.def_path_str_with_args(def_id, args)
             ),
@@ -533,7 +618,7 @@ impl<'tcx> Instance<'tcx> {
         // Use either `resolve_closure` or `resolve_for_vtable`
         assert!(!tcx.is_closure_like(def_id), "Called `resolve_for_fn_ptr` on closure: {def_id:?}");
         let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::FnPtr);
-        Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| {
+        Instance::try_resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| {
             match resolved.def {
                 InstanceKind::Item(def) if resolved.def.requires_caller_location(tcx) => {
                     debug!(" => fn pointer created for function with #[track_caller]");
@@ -571,77 +656,82 @@ impl<'tcx> Instance<'tcx> {
         })
     }
 
-    pub fn resolve_for_vtable(
+    pub fn expect_resolve_for_vtable(
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         def_id: DefId,
         args: GenericArgsRef<'tcx>,
-    ) -> Option<Instance<'tcx>> {
+        span: Span,
+    ) -> Instance<'tcx> {
         debug!("resolve_for_vtable(def_id={:?}, args={:?})", def_id, args);
         let fn_sig = tcx.fn_sig(def_id).instantiate_identity();
         let is_vtable_shim = !fn_sig.inputs().skip_binder().is_empty()
             && fn_sig.input(0).skip_binder().is_param(0)
             && tcx.generics_of(def_id).has_self;
+
         if is_vtable_shim {
             debug!(" => associated item with unsizeable self: Self");
-            Some(Instance { def: InstanceKind::VTableShim(def_id), args })
-        } else {
-            let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable);
-            Instance::resolve(tcx, param_env, def_id, args).ok().flatten().map(|mut resolved| {
-                match resolved.def {
-                    InstanceKind::Item(def) => {
-                        // We need to generate a shim when we cannot guarantee that
-                        // the caller of a trait object method will be aware of
-                        // `#[track_caller]` - this ensures that the caller
-                        // and callee ABI will always match.
-                        //
-                        // The shim is generated when all of these conditions are met:
-                        //
-                        // 1) The underlying method expects a caller location parameter
-                        // in the ABI
-                        if resolved.def.requires_caller_location(tcx)
-                            // 2) The caller location parameter comes from having `#[track_caller]`
-                            // on the implementation, and *not* on the trait method.
-                            && !tcx.should_inherit_track_caller(def)
-                            // If the method implementation comes from the trait definition itself
-                            // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`),
-                            // then we don't need to generate a shim. This check is needed because
-                            // `should_inherit_track_caller` returns `false` if our method
-                            // implementation comes from the trait block, and not an impl block
-                            && !matches!(
-                                tcx.opt_associated_item(def),
-                                Some(ty::AssocItem {
-                                    container: ty::AssocItemContainer::TraitContainer,
-                                    ..
-                                })
-                            )
-                        {
-                            if tcx.is_closure_like(def) {
-                                debug!(" => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}",
-                                       def, def_id, args);
-
-                                // Create a shim for the `FnOnce/FnMut/Fn` method we are calling
-                                // - unlike functions, invoking a closure always goes through a
-                                // trait.
-                                resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args };
-                            } else {
-                                debug!(
-                                    " => vtable fn pointer created for function with #[track_caller]: {:?}", def
-                                );
-                                resolved.def = InstanceKind::ReifyShim(def, reason);
-                            }
-                        }
-                    }
-                    InstanceKind::Virtual(def_id, _) => {
-                        debug!(" => vtable fn pointer created for virtual call");
-                        resolved.def = InstanceKind::ReifyShim(def_id, reason)
+            return Instance { def: InstanceKind::VTableShim(def_id), args };
+        }
+
+        let mut resolved = Instance::expect_resolve(tcx, param_env, def_id, args, span);
+
+        let reason = tcx.sess.is_sanitizer_kcfi_enabled().then_some(ReifyReason::Vtable);
+        match resolved.def {
+            InstanceKind::Item(def) => {
+                // We need to generate a shim when we cannot guarantee that
+                // the caller of a trait object method will be aware of
+                // `#[track_caller]` - this ensures that the caller
+                // and callee ABI will always match.
+                //
+                // The shim is generated when all of these conditions are met:
+                //
+                // 1) The underlying method expects a caller location parameter
+                // in the ABI
+                if resolved.def.requires_caller_location(tcx)
+                        // 2) The caller location parameter comes from having `#[track_caller]`
+                        // on the implementation, and *not* on the trait method.
+                        && !tcx.should_inherit_track_caller(def)
+                        // If the method implementation comes from the trait definition itself
+                        // (e.g. `trait Foo { #[track_caller] my_fn() { /* impl */ } }`),
+                        // then we don't need to generate a shim. This check is needed because
+                        // `should_inherit_track_caller` returns `false` if our method
+                        // implementation comes from the trait block, and not an impl block
+                        && !matches!(
+                            tcx.opt_associated_item(def),
+                            Some(ty::AssocItem {
+                                container: ty::AssocItemContainer::TraitContainer,
+                                ..
+                            })
+                        )
+                {
+                    if tcx.is_closure_like(def) {
+                        debug!(
+                            " => vtable fn pointer created for closure with #[track_caller]: {:?} for method {:?} {:?}",
+                            def, def_id, args
+                        );
+
+                        // Create a shim for the `FnOnce/FnMut/Fn` method we are calling
+                        // - unlike functions, invoking a closure always goes through a
+                        // trait.
+                        resolved = Instance { def: InstanceKind::ReifyShim(def_id, reason), args };
+                    } else {
+                        debug!(
+                            " => vtable fn pointer created for function with #[track_caller]: {:?}",
+                            def
+                        );
+                        resolved.def = InstanceKind::ReifyShim(def, reason);
                     }
-                    _ => {}
                 }
-
-                resolved
-            })
+            }
+            InstanceKind::Virtual(def_id, _) => {
+                debug!(" => vtable fn pointer created for virtual call");
+                resolved.def = InstanceKind::ReifyShim(def_id, reason)
+            }
+            _ => {}
         }
+
+        resolved
     }
 
     pub fn resolve_closure(
@@ -661,13 +751,25 @@ impl<'tcx> Instance<'tcx> {
     pub fn resolve_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
         let def_id = tcx.require_lang_item(LangItem::DropInPlace, None);
         let args = tcx.mk_args(&[ty.into()]);
-        Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
+        Instance::expect_resolve(
+            tcx,
+            ty::ParamEnv::reveal_all(),
+            def_id,
+            args,
+            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+        )
     }
 
     pub fn resolve_async_drop_in_place(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::Instance<'tcx> {
         let def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, None);
         let args = tcx.mk_args(&[ty.into()]);
-        Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
+        Instance::expect_resolve(
+            tcx,
+            ty::ParamEnv::reveal_all(),
+            def_id,
+            args,
+            ty.ty_adt_def().and_then(|adt| tcx.hir().span_if_local(adt.did())).unwrap_or(DUMMY_SP),
+        )
     }
 
     #[instrument(level = "debug", skip(tcx), ret)]
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 9c2bfc12a18..e1bd3ad8ad3 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -148,6 +148,7 @@ mod closure;
 mod consts;
 mod context;
 mod diagnostics;
+mod elaborate_impl;
 mod erase_regions;
 mod generic_args;
 mod generics;
@@ -1608,7 +1609,7 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
-    /// If the def-id is an associated type that was desugared from a
+    /// If the `def_id` is an associated type that was desugared from a
     /// return-position `impl Trait` from a trait, then provide the source info
     /// about where that RPITIT came from.
     pub fn opt_rpitit_info(self, def_id: DefId) -> Option<ImplTraitInTraitData> {
@@ -1619,6 +1620,16 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    /// Whether the `def_id` is an associated type that was desugared from a
+    /// `#[const_trait]` or `impl_const`.
+    pub fn is_effects_desugared_assoc_ty(self, def_id: DefId) -> bool {
+        if let DefKind::AssocTy = self.def_kind(def_id) {
+            self.associated_item(def_id).is_effects_desugaring
+        } else {
+            false
+        }
+    }
+
     pub fn find_field_index(self, ident: Ident, variant: &VariantDef) -> Option<FieldIdx> {
         variant.fields.iter_enumerated().find_map(|(i, field)| {
             self.hygienic_eq(ident, field.ident(self), variant.def_id).then_some(i)
@@ -1957,8 +1968,13 @@ impl<'tcx> TyCtxt<'tcx> {
     }
 
     #[inline]
+    pub fn is_const_trait(self, def_id: DefId) -> bool {
+        self.trait_def(def_id).constness == hir::Constness::Const
+    }
+
+    #[inline]
     pub fn is_const_default_method(self, def_id: DefId) -> bool {
-        matches!(self.trait_of_item(def_id), Some(trait_id) if self.has_attr(trait_id, sym::const_trait))
+        matches!(self.trait_of_item(def_id), Some(trait_id) if self.is_const_trait(trait_id))
     }
 
     pub fn impl_method_has_trait_impl_trait_tys(self, def_id: DefId) -> bool {
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index e9b37503bb3..5d6352c57ce 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -46,6 +46,10 @@ pub struct Predicate<'tcx>(
 );
 
 impl<'tcx> rustc_type_ir::inherent::Predicate<TyCtxt<'tcx>> for Predicate<'tcx> {
+    fn as_clause(self) -> Option<ty::Clause<'tcx>> {
+        self.as_clause()
+    }
+
     fn is_coinductive(self, interner: TyCtxt<'tcx>) -> bool {
         self.is_coinductive(interner)
     }
@@ -173,7 +177,11 @@ pub struct Clause<'tcx>(
     pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
 );
 
-impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {}
+impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {
+    fn instantiate_supertrait(self, tcx: TyCtxt<'tcx>, trait_ref: ty::PolyTraitRef<'tcx>) -> Self {
+        self.instantiate_supertrait(tcx, trait_ref)
+    }
+}
 
 impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> {
     type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>;
@@ -341,6 +349,14 @@ impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
             _ => None,
         })
     }
+
+    pub fn without_auto_traits(
+        &self,
+    ) -> impl Iterator<Item = ty::PolyExistentialPredicate<'tcx>> + '_ {
+        self.iter().filter(|predicate| {
+            !matches!(predicate.as_ref().skip_binder(), ExistentialPredicate::AutoTrait(_))
+        })
+    }
 }
 
 pub type PolyTraitRef<'tcx> = ty::Binder<'tcx, TraitRef<'tcx>>;
diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs
index 3c27df9529a..c165790548d 100644
--- a/compiler/rustc_middle/src/ty/print/mod.rs
+++ b/compiler/rustc_middle/src/ty/print/mod.rs
@@ -1,5 +1,7 @@
+use std::path::PathBuf;
+
 use crate::ty::GenericArg;
-use crate::ty::{self, Ty, TyCtxt};
+use crate::ty::{self, ShortInstance, Ty, TyCtxt};
 
 use hir::def::Namespace;
 use rustc_data_structures::fx::FxHashSet;
@@ -356,3 +358,31 @@ where
         with_no_trimmed_paths!(Self::print(t, fmt))
     }
 }
+
+/// Format instance name that is already known to be too long for rustc.
+/// Show only the first 2 types if it is longer than 32 characters to avoid blasting
+/// the user's terminal with thousands of lines of type-name.
+///
+/// If the type name is longer than before+after, it will be written to a file.
+pub fn shrunk_instance_name<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    instance: ty::Instance<'tcx>,
+) -> (String, Option<PathBuf>) {
+    let s = instance.to_string();
+
+    // Only use the shrunk version if it's really shorter.
+    // This also avoids the case where before and after slices overlap.
+    if s.chars().nth(33).is_some() {
+        let shrunk = format!("{}", ShortInstance(instance, 4));
+        if shrunk == s {
+            return (s, None);
+        }
+
+        let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None);
+        let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
+
+        (shrunk, written_to_path)
+    } else {
+        (s, None)
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 19700353f59..df080b2887b 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -1,7 +1,6 @@
 use crate::mir::interpret::{AllocRange, GlobalAlloc, Pointer, Provenance, Scalar};
 use crate::query::IntoQueryParam;
 use crate::query::Providers;
-use crate::traits::util::{super_predicates_for_pretty_printing, supertraits_for_pretty_printing};
 use crate::ty::GenericArgKind;
 use crate::ty::{
     ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable,
@@ -23,6 +22,7 @@ use rustc_span::symbol::{kw, Ident, Symbol};
 use rustc_span::FileNameDisplayPreference;
 use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
+use rustc_type_ir::{elaborate, Upcast as _};
 use smallvec::SmallVec;
 
 use std::cell::Cell;
@@ -1255,14 +1255,14 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 entry.has_fn_once = true;
                 return;
             } else if self.tcx().is_lang_item(trait_def_id, LangItem::FnMut) {
-                let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref)
+                let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref)
                     .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
                     .unwrap();
 
                 fn_traits.entry(super_trait_ref).or_default().fn_mut_trait_ref = Some(trait_ref);
                 return;
             } else if self.tcx().is_lang_item(trait_def_id, LangItem::Fn) {
-                let super_trait_ref = supertraits_for_pretty_printing(self.tcx(), trait_ref)
+                let super_trait_ref = elaborate::supertraits(self.tcx(), trait_ref)
                     .find(|super_trait_ref| super_trait_ref.def_id() == fn_once_trait)
                     .unwrap();
 
@@ -1343,10 +1343,11 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                     let bound_principal_with_self = bound_principal
                         .with_self_ty(cx.tcx(), cx.tcx().types.trait_object_dummy_self);
 
-                    let super_projections: Vec<_> =
-                        super_predicates_for_pretty_printing(cx.tcx(), bound_principal_with_self)
-                            .filter_map(|clause| clause.as_projection_clause())
-                            .collect();
+                    let clause: ty::Clause<'tcx> = bound_principal_with_self.upcast(cx.tcx());
+                    let super_projections: Vec<_> = elaborate::elaborate(cx.tcx(), [clause])
+                        .filter_only_self()
+                        .filter_map(|clause| clause.as_projection_clause())
+                        .collect();
 
                     let mut projections: Vec<_> = predicates
                         .projection_bounds()
@@ -1667,7 +1668,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                                 Some(GlobalAlloc::Static(def_id)) => {
                                     p!(write("<static({:?})>", def_id))
                                 }
-                                Some(GlobalAlloc::Function(_)) => p!("<function>"),
+                                Some(GlobalAlloc::Function { .. }) => p!("<function>"),
                                 Some(GlobalAlloc::VTable(..)) => p!("<vtable>"),
                                 None => p!("<dangling pointer>"),
                             }
@@ -1679,7 +1680,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::FnPtr(_) => {
                 // FIXME: We should probably have a helper method to share code with the "Byte strings"
                 // printing above (which also has to handle pointers to all sorts of things).
-                if let Some(GlobalAlloc::Function(instance)) =
+                if let Some(GlobalAlloc::Function { instance, .. }) =
                     self.tcx().try_get_global_alloc(prov.alloc_id())
                 {
                     self.typed_value(
@@ -1710,22 +1711,24 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::Bool if int == ScalarInt::FALSE => p!("false"),
             ty::Bool if int == ScalarInt::TRUE => p!("true"),
             // Float
-            ty::Float(ty::FloatTy::F16) => {
-                let val = Half::try_from(int).unwrap();
-                p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" }))
-            }
-            ty::Float(ty::FloatTy::F32) => {
-                let val = Single::try_from(int).unwrap();
-                p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" }))
-            }
-            ty::Float(ty::FloatTy::F64) => {
-                let val = Double::try_from(int).unwrap();
-                p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" }))
-            }
-            ty::Float(ty::FloatTy::F128) => {
-                let val = Quad::try_from(int).unwrap();
-                p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" }))
-            }
+            ty::Float(fty) => match fty {
+                ty::FloatTy::F16 => {
+                    let val = Half::try_from(int).unwrap();
+                    p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" }))
+                }
+                ty::FloatTy::F32 => {
+                    let val = Single::try_from(int).unwrap();
+                    p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" }))
+                }
+                ty::FloatTy::F64 => {
+                    let val = Double::try_from(int).unwrap();
+                    p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" }))
+                }
+                ty::FloatTy::F128 => {
+                    let val = Quad::try_from(int).unwrap();
+                    p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" }))
+                }
+            },
             // Int
             ty::Uint(_) | ty::Int(_) => {
                 let int =
diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs
index b169d672a84..ebf0d7ed737 100644
--- a/compiler/rustc_middle/src/ty/relate.rs
+++ b/compiler/rustc_middle/src/ty/relate.rs
@@ -10,18 +10,6 @@ use crate::ty::{self as ty, Ty, TyCtxt};
 
 pub type RelateResult<'tcx, T> = rustc_type_ir::relate::RelateResult<TyCtxt<'tcx>, T>;
 
-/// Whether aliases should be related structurally or not. Used
-/// to adjust the behavior of generalization and combine.
-///
-/// This should always be `No` unless in a few special-cases when
-/// instantiating canonical responses and in the new solver. Each
-/// such case should have a comment explaining why it is used.
-#[derive(Debug, Copy, Clone)]
-pub enum StructurallyRelateAliases {
-    Yes,
-    No,
-}
-
 impl<'tcx> Relate<TyCtxt<'tcx>> for ty::ImplSubject<'tcx> {
     #[inline]
     fn relate<R: TypeRelation<TyCtxt<'tcx>>>(
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index ff40a726fbc..d2b444a066b 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -68,6 +68,10 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> {
     const RETURNED: usize = 1;
     /// Coroutine has been poisoned.
     const POISONED: usize = 2;
+    /// Number of variants to reserve in coroutine state. Corresponds to
+    /// `UNRESUMED` (beginning of a coroutine) and `RETURNED`/`POISONED`
+    /// (end of a coroutine) states.
+    const RESERVED_VARIANTS: usize = 3;
 
     const UNRESUMED_NAME: &'static str = "Unresumed";
     const RETURNED_NAME: &'static str = "Returned";
@@ -116,7 +120,7 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> {
             Self::UNRESUMED => Cow::from(Self::UNRESUMED_NAME),
             Self::RETURNED => Cow::from(Self::RETURNED_NAME),
             Self::POISONED => Cow::from(Self::POISONED_NAME),
-            _ => Cow::from(format!("Suspend{}", v.as_usize() - 3)),
+            _ => Cow::from(format!("Suspend{}", v.as_usize() - Self::RESERVED_VARIANTS)),
         }
     }
 
@@ -807,6 +811,14 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
         Ty::new_var(tcx, vid)
     }
 
+    fn new_param(tcx: TyCtxt<'tcx>, param: ty::ParamTy) -> Self {
+        Ty::new_param(tcx, param.index, param.name)
+    }
+
+    fn new_placeholder(tcx: TyCtxt<'tcx>, placeholder: ty::PlaceholderType) -> Self {
+        Ty::new_placeholder(tcx, placeholder)
+    }
+
     fn new_bound(interner: TyCtxt<'tcx>, debruijn: ty::DebruijnIndex, var: ty::BoundTy) -> Self {
         Ty::new_bound(interner, debruijn, var)
     }
diff --git a/compiler/rustc_middle/src/ty/trait_def.rs b/compiler/rustc_middle/src/ty/trait_def.rs
index 4dba97c3b5b..da5860043c9 100644
--- a/compiler/rustc_middle/src/ty/trait_def.rs
+++ b/compiler/rustc_middle/src/ty/trait_def.rs
@@ -18,6 +18,9 @@ pub struct TraitDef {
 
     pub safety: hir::Safety,
 
+    /// Whether this trait has been annotated with `#[const_trait]`.
+    pub constness: hir::Constness,
+
     /// If `true`, then this trait had the `#[rustc_paren_sugar]`
     /// attribute, indicating that it should be used with `Foo()`
     /// sugar. This is a temporary thing -- eventually any trait will
@@ -31,7 +34,7 @@ pub struct TraitDef {
     /// and thus `impl`s of it are allowed to overlap.
     pub is_marker: bool,
 
-    /// If `true`, then this trait has to `#[rustc_coinductive]` attribute or
+    /// If `true`, then this trait has the `#[rustc_coinductive]` attribute or
     /// is an auto trait. This indicates that trait solver cycles involving an
     /// `X: ThisTrait` goal are accepted.
     ///
@@ -40,6 +43,11 @@ pub struct TraitDef {
     /// also have already switched to the new trait solver.
     pub is_coinductive: bool,
 
+    /// If `true`, then this trait has the `#[fundamental]` attribute. This
+    /// affects how conherence computes whether a trait may have trait implementations
+    /// added in the future.
+    pub is_fundamental: bool,
+
     /// If `true`, then this trait has the `#[rustc_skip_during_method_dispatch(array)]`
     /// attribute, indicating that editions before 2021 should not consider this trait
     /// during method dispatch if the receiver is an array.
diff --git a/compiler/rustc_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs
index dc3c84f9e43..466c3b93f8e 100644
--- a/compiler/rustc_middle/src/ty/vtable.rs
+++ b/compiler/rustc_middle/src/ty/vtable.rs
@@ -3,8 +3,6 @@ use std::fmt;
 use crate::mir::interpret::{alloc_range, AllocId, Allocation, Pointer, Scalar};
 use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt};
 use rustc_ast::Mutability;
-use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::DefId;
 use rustc_macros::HashStable;
 
 #[derive(Clone, Copy, PartialEq, HashStable)]
@@ -42,45 +40,12 @@ impl<'tcx> fmt::Debug for VtblEntry<'tcx> {
 impl<'tcx> TyCtxt<'tcx> {
     pub const COMMON_VTABLE_ENTRIES: &'tcx [VtblEntry<'tcx>] =
         &[VtblEntry::MetadataDropInPlace, VtblEntry::MetadataSize, VtblEntry::MetadataAlign];
-
-    pub fn supertrait_def_ids(self, trait_def_id: DefId) -> SupertraitDefIds<'tcx> {
-        SupertraitDefIds {
-            tcx: self,
-            stack: vec![trait_def_id],
-            visited: Some(trait_def_id).into_iter().collect(),
-        }
-    }
 }
 
 pub const COMMON_VTABLE_ENTRIES_DROPINPLACE: usize = 0;
 pub const COMMON_VTABLE_ENTRIES_SIZE: usize = 1;
 pub const COMMON_VTABLE_ENTRIES_ALIGN: usize = 2;
 
-pub struct SupertraitDefIds<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    stack: Vec<DefId>,
-    visited: FxHashSet<DefId>,
-}
-
-impl Iterator for SupertraitDefIds<'_> {
-    type Item = DefId;
-
-    fn next(&mut self) -> Option<DefId> {
-        let def_id = self.stack.pop()?;
-        let predicates = self.tcx.super_predicates_of(def_id);
-        let visited = &mut self.visited;
-        self.stack.extend(
-            predicates
-                .predicates
-                .iter()
-                .filter_map(|(pred, _)| pred.as_trait_clause())
-                .map(|trait_ref| trait_ref.def_id())
-                .filter(|&super_def_id| visited.insert(super_def_id)),
-        );
-        Some(def_id)
-    }
-}
-
 // Note that we don't have access to a self type here, this has to be purely based on the trait (and
 // supertrait) definitions. That means we can't call into the same vtable_entries code since that
 // returns a specific instantiation (e.g., with Vacant slots when bounds aren't satisfied). The goal
diff --git a/compiler/rustc_middle/src/ty/walk.rs b/compiler/rustc_middle/src/ty/walk.rs
index e0f204a687f..efcaf89081f 100644
--- a/compiler/rustc_middle/src/ty/walk.rs
+++ b/compiler/rustc_middle/src/ty/walk.rs
@@ -78,23 +78,6 @@ impl<'tcx> GenericArg<'tcx> {
     pub fn walk(self) -> TypeWalker<'tcx> {
         TypeWalker::new(self)
     }
-
-    /// Iterator that walks the immediate children of `self`. Hence
-    /// `Foo<Bar<i32>, u32>` yields the sequence `[Bar<i32>, u32]`
-    /// (but not `i32`, like `walk`).
-    ///
-    /// Iterator only walks items once.
-    /// It accepts visited set, updates it with all visited types
-    /// and skips any types that are already there.
-    pub fn walk_shallow(
-        self,
-        visited: &mut SsoHashSet<GenericArg<'tcx>>,
-    ) -> impl Iterator<Item = GenericArg<'tcx>> {
-        let mut stack = SmallVec::new();
-        push_inner(&mut stack, self);
-        stack.retain(|a| visited.insert(*a));
-        stack.into_iter()
-    }
 }
 
 impl<'tcx> Ty<'tcx> {
diff --git a/compiler/rustc_middle/src/util/call_kind.rs b/compiler/rustc_middle/src/util/call_kind.rs
index dc1d73684f4..0815c291173 100644
--- a/compiler/rustc_middle/src/util/call_kind.rs
+++ b/compiler/rustc_middle/src/util/call_kind.rs
@@ -98,7 +98,7 @@ pub fn call_kind<'tcx>(
         Some(CallKind::Operator { self_arg, trait_id, self_ty: method_args.type_at(0) })
     } else if is_deref {
         let deref_target = tcx.get_diagnostic_item(sym::deref_target).and_then(|deref_target| {
-            Instance::resolve(tcx, param_env, deref_target, method_args).transpose()
+            Instance::try_resolve(tcx, param_env, deref_target, method_args).transpose()
         });
         if let Some(Ok(instance)) = deref_target {
             let deref_target_ty = instance.ty(tcx, param_env);
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index c1d645aa42c..5ccbd7c59cf 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -1,3 +1,4 @@
+use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
 use crate::build::ForGuard::OutsideGuard;
 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use rustc_middle::middle::region::Scope;
@@ -189,38 +190,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                     let initializer_span = this.thir[*initializer].span;
                     let scope = (*init_scope, source_info);
-                    let failure = unpack!(
-                        block = this.in_scope(scope, *lint_level, |this| {
-                            this.declare_bindings(
-                                visibility_scope,
-                                remainder_span,
-                                pattern,
-                                None,
-                                Some((Some(&destination), initializer_span)),
-                            );
-                            this.visit_primary_bindings(
-                                pattern,
-                                UserTypeProjections::none(),
-                                &mut |this, _, _, node, span, _, _| {
-                                    this.storage_live_binding(
-                                        block,
-                                        node,
-                                        span,
-                                        OutsideGuard,
-                                        true,
-                                    );
-                                },
-                            );
-                            this.ast_let_else(
-                                block,
-                                *initializer,
-                                initializer_span,
-                                *else_block,
-                                &last_remainder_scope,
-                                pattern,
-                            )
-                        })
-                    );
+                    let failure_and_block = this.in_scope(scope, *lint_level, |this| {
+                        this.declare_bindings(
+                            visibility_scope,
+                            remainder_span,
+                            pattern,
+                            None,
+                            Some((Some(&destination), initializer_span)),
+                        );
+                        this.visit_primary_bindings(
+                            pattern,
+                            UserTypeProjections::none(),
+                            &mut |this, _, _, node, span, _, _| {
+                                this.storage_live_binding(
+                                    block,
+                                    node,
+                                    span,
+                                    OutsideGuard,
+                                    ScheduleDrops::Yes,
+                                );
+                            },
+                        );
+                        let else_block_span = this.thir[*else_block].span;
+                        let (matching, failure) =
+                            this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
+                                this.lower_let_expr(
+                                    block,
+                                    *initializer,
+                                    pattern,
+                                    None,
+                                    initializer_span,
+                                    DeclareLetBindings::No,
+                                    EmitStorageLive::No,
+                                )
+                            });
+                        matching.and(failure)
+                    });
+                    let failure = unpack!(block = failure_and_block);
                     this.cfg.goto(failure, source_info, failure_entry);
 
                     if let Some(source_scope) = visibility_scope {
@@ -292,7 +298,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             pattern,
                             UserTypeProjections::none(),
                             &mut |this, _, _, node, span, _, _| {
-                                this.storage_live_binding(block, node, span, OutsideGuard, true);
+                                this.storage_live_binding(
+                                    block,
+                                    node,
+                                    span,
+                                    OutsideGuard,
+                                    ScheduleDrops::Yes,
+                                );
                                 this.schedule_drop_for_binding(node, span, OutsideGuard);
                             },
                         )
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo.rs b/compiler/rustc_mir_build/src/build/coverageinfo.rs
index 876faca5172..204ee45bfa2 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo.rs
@@ -2,7 +2,7 @@ use std::assert_matches::assert_matches;
 use std::collections::hash_map::Entry;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageKind};
+use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind};
 use rustc_middle::mir::{self, BasicBlock, SourceInfo, UnOp};
 use rustc_middle::thir::{ExprId, ExprKind, Pat, Thir};
 use rustc_middle::ty::TyCtxt;
@@ -13,16 +13,25 @@ use crate::build::{Builder, CFG};
 
 mod mcdc;
 
-pub(crate) struct BranchInfoBuilder {
+/// Collects coverage-related information during MIR building, to eventually be
+/// turned into a function's [`CoverageInfoHi`] when MIR building is complete.
+pub(crate) struct CoverageInfoBuilder {
     /// Maps condition expressions to their enclosing `!`, for better instrumentation.
     nots: FxHashMap<ExprId, NotInfo>,
 
     markers: BlockMarkerGen,
-    branch_spans: Vec<BranchSpan>,
 
+    /// Present if branch coverage is enabled.
+    branch_info: Option<BranchInfo>,
+    /// Present if MC/DC coverage is enabled.
     mcdc_info: Option<MCDCInfoBuilder>,
 }
 
+#[derive(Default)]
+struct BranchInfo {
+    branch_spans: Vec<BranchSpan>,
+}
+
 #[derive(Clone, Copy)]
 struct NotInfo {
     /// When visiting the associated expression as a branch condition, treat this
@@ -62,20 +71,20 @@ impl BlockMarkerGen {
     }
 }
 
-impl BranchInfoBuilder {
-    /// Creates a new branch info builder, but only if branch coverage instrumentation
+impl CoverageInfoBuilder {
+    /// Creates a new coverage info builder, but only if coverage instrumentation
     /// is enabled and `def_id` represents a function that is eligible for coverage.
     pub(crate) fn new_if_enabled(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<Self> {
-        if tcx.sess.instrument_coverage_branch() && tcx.is_eligible_for_coverage(def_id) {
-            Some(Self {
-                nots: FxHashMap::default(),
-                markers: BlockMarkerGen::default(),
-                branch_spans: vec![],
-                mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new),
-            })
-        } else {
-            None
+        if !tcx.sess.instrument_coverage() || !tcx.is_eligible_for_coverage(def_id) {
+            return None;
         }
+
+        Some(Self {
+            nots: FxHashMap::default(),
+            markers: BlockMarkerGen::default(),
+            branch_info: tcx.sess.instrument_coverage_branch().then(BranchInfo::default),
+            mcdc_info: tcx.sess.instrument_coverage_mcdc().then(MCDCInfoBuilder::new),
+        })
     }
 
     /// Unary `!` expressions inside an `if` condition are lowered by lowering
@@ -88,6 +97,12 @@ impl BranchInfoBuilder {
     pub(crate) fn visit_unary_not(&mut self, thir: &Thir<'_>, unary_not: ExprId) {
         assert_matches!(thir[unary_not].kind, ExprKind::Unary { op: UnOp::Not, .. });
 
+        // The information collected by this visitor is only needed when branch
+        // coverage or higher is enabled.
+        if self.branch_info.is_none() {
+            return;
+        }
+
         self.visit_with_not_info(
             thir,
             unary_not,
@@ -137,40 +152,40 @@ impl BranchInfoBuilder {
                 false_block,
                 inject_block_marker,
             );
-        } else {
-            let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block);
-            let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block);
-
-            self.branch_spans.push(BranchSpan {
-                span: source_info.span,
-                true_marker,
-                false_marker,
-            });
+            return;
         }
+
+        // Bail out if branch coverage is not enabled.
+        let Some(branch_info) = self.branch_info.as_mut() else { return };
+
+        let true_marker = self.markers.inject_block_marker(cfg, source_info, true_block);
+        let false_marker = self.markers.inject_block_marker(cfg, source_info, false_block);
+
+        branch_info.branch_spans.push(BranchSpan {
+            span: source_info.span,
+            true_marker,
+            false_marker,
+        });
     }
 
-    pub(crate) fn into_done(self) -> Option<Box<mir::coverage::BranchInfo>> {
-        let Self {
-            nots: _,
-            markers: BlockMarkerGen { num_block_markers },
-            branch_spans,
-            mcdc_info,
-        } = self;
+    pub(crate) fn into_done(self) -> Box<CoverageInfoHi> {
+        let Self { nots: _, markers: BlockMarkerGen { num_block_markers }, branch_info, mcdc_info } =
+            self;
 
-        if num_block_markers == 0 {
-            assert!(branch_spans.is_empty());
-            return None;
-        }
+        let branch_spans =
+            branch_info.map(|branch_info| branch_info.branch_spans).unwrap_or_default();
 
         let (mcdc_decision_spans, mcdc_branch_spans) =
             mcdc_info.map(MCDCInfoBuilder::into_done).unwrap_or_default();
 
-        Some(Box::new(mir::coverage::BranchInfo {
+        // For simplicity, always return an info struct (without Option), even
+        // if there's nothing interesting in it.
+        Box::new(CoverageInfoHi {
             num_block_markers,
             branch_spans,
             mcdc_branch_spans,
             mcdc_decision_spans,
-        }))
+        })
     }
 }
 
@@ -184,7 +199,7 @@ impl<'tcx> Builder<'_, 'tcx> {
         block: &mut BasicBlock,
     ) {
         // Bail out if condition coverage is not enabled for this function.
-        let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
+        let Some(coverage_info) = self.coverage_info.as_mut() else { return };
         if !self.tcx.sess.instrument_coverage_condition() {
             return;
         };
@@ -224,7 +239,7 @@ impl<'tcx> Builder<'_, 'tcx> {
         );
 
         // Separate path for handling branches when MC/DC is enabled.
-        branch_info.register_two_way_branch(
+        coverage_info.register_two_way_branch(
             self.tcx,
             &mut self.cfg,
             source_info,
@@ -247,12 +262,12 @@ impl<'tcx> Builder<'_, 'tcx> {
         mut then_block: BasicBlock,
         mut else_block: BasicBlock,
     ) {
-        // Bail out if branch coverage is not enabled for this function.
-        let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
+        // Bail out if coverage is not enabled for this function.
+        let Some(coverage_info) = self.coverage_info.as_mut() else { return };
 
         // If this condition expression is nested within one or more `!` expressions,
         // replace it with the enclosing `!` collected by `visit_unary_not`.
-        if let Some(&NotInfo { enclosing_not, is_flipped }) = branch_info.nots.get(&expr_id) {
+        if let Some(&NotInfo { enclosing_not, is_flipped }) = coverage_info.nots.get(&expr_id) {
             expr_id = enclosing_not;
             if is_flipped {
                 std::mem::swap(&mut then_block, &mut else_block);
@@ -261,7 +276,7 @@ impl<'tcx> Builder<'_, 'tcx> {
 
         let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };
 
-        branch_info.register_two_way_branch(
+        coverage_info.register_two_way_branch(
             self.tcx,
             &mut self.cfg,
             source_info,
@@ -280,13 +295,11 @@ impl<'tcx> Builder<'_, 'tcx> {
         true_block: BasicBlock,
         false_block: BasicBlock,
     ) {
-        // Bail out if branch coverage is not enabled for this function.
-        let Some(branch_info) = self.coverage_branch_info.as_mut() else { return };
-
-        // FIXME(#124144) This may need special handling when MC/DC is enabled.
+        // Bail out if coverage is not enabled for this function.
+        let Some(coverage_info) = self.coverage_info.as_mut() else { return };
 
         let source_info = SourceInfo { span: pattern.span, scope: self.source_scope };
-        branch_info.register_two_way_branch(
+        coverage_info.register_two_way_branch(
             self.tcx,
             &mut self.cfg,
             source_info,
diff --git a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
index f97e9ef60a2..3aa6e708476 100644
--- a/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
+++ b/compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs
@@ -250,24 +250,24 @@ impl MCDCInfoBuilder {
 
 impl Builder<'_, '_> {
     pub(crate) fn visit_coverage_branch_operation(&mut self, logical_op: LogicalOp, span: Span) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        if let Some(coverage_info) = self.coverage_info.as_mut()
+            && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
         {
             mcdc_info.state.record_conditions(logical_op, span);
         }
     }
 
     pub(crate) fn mcdc_increment_depth_if_enabled(&mut self) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        if let Some(coverage_info) = self.coverage_info.as_mut()
+            && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
         {
             mcdc_info.state.decision_ctx_stack.push(MCDCDecisionCtx::default());
         };
     }
 
     pub(crate) fn mcdc_decrement_depth_if_enabled(&mut self) {
-        if let Some(branch_info) = self.coverage_branch_info.as_mut()
-            && let Some(mcdc_info) = branch_info.mcdc_info.as_mut()
+        if let Some(coverage_info) = self.coverage_info.as_mut()
+            && let Some(mcdc_info) = coverage_info.mcdc_info.as_mut()
         {
             if mcdc_info.state.decision_ctx_stack.pop().is_none() {
                 bug!("Unexpected empty decision stack");
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs
index a0a512a2eff..f6ebcbcbdc9 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/build/custom/mod.rs
@@ -62,7 +62,7 @@ pub(super) fn build_custom_mir<'tcx>(
         tainted_by_errors: None,
         injection_phase: None,
         pass_count: 0,
-        coverage_branch_info: None,
+        coverage_info_hi: None,
         function_coverage_info: None,
     };
 
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index 4b62afa61bb..91a3b53cc79 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -130,7 +130,7 @@ fn convert_to_hir_projections_and_truncate_for_capture(
 /// Eg: 1. `foo.x` which is represented using `projections=[Field(x)]` is an ancestor of
 ///        `foo.x.y` which is represented using `projections=[Field(x), Field(y)]`.
 ///        Note both `foo.x` and `foo.x.y` start off of the same root variable `foo`.
-///     2. Since we only look at the projections here function will return `bar.x` as an a valid
+///     2. Since we only look at the projections here function will return `bar.x` as a valid
 ///        ancestor of `foo.x.y`. It's the caller's responsibility to ensure that both projections
 ///        list are being applied to the same root variable.
 fn is_ancestor_or_same_capture(
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 76bdc26a501..942c69b5c0a 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -1,6 +1,7 @@
 //! See docs in build/expr/mod.rs
 
 use crate::build::expr::category::{Category, RvalueFunc};
+use crate::build::matches::DeclareLetBindings;
 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
 use rustc_ast::InlineAsmOptions;
 use rustc_data_structures::fx::FxHashMap;
@@ -86,7 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                     cond,
                                     Some(condition_scope), // Temp scope
                                     source_info,
-                                    true, // Declare `let` bindings normally
+                                    DeclareLetBindings::Yes, // Declare `let` bindings normally
                                 ));
 
                                 // Lower the `then` arm into its block.
@@ -163,7 +164,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                             source_info,
                             // This flag controls how inner `let` expressions are lowered,
                             // but either way there shouldn't be any of those in here.
-                            true,
+                            DeclareLetBindings::LetNotPermitted,
                         )
                     });
                 let (short_circuit, continuation, constant) = match op {
diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs
index 2bdeb579a02..88b76c46c90 100644
--- a/compiler/rustc_mir_build/src/build/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs
@@ -2,7 +2,9 @@ use crate::build::scope::BreakableTarget;
 use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
 use rustc_middle::middle::region;
 use rustc_middle::mir::*;
+use rustc_middle::span_bug;
 use rustc_middle::thir::*;
+use rustc_span::source_map::Spanned;
 use tracing::debug;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -91,9 +93,42 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             ExprKind::Return { value } => {
                 this.break_scope(block, value, BreakableTarget::Return, source_info)
             }
-            // FIXME(explicit_tail_calls): properly lower tail calls here
             ExprKind::Become { value } => {
-                this.break_scope(block, Some(value), BreakableTarget::Return, source_info)
+                let v = &this.thir[value];
+                let ExprKind::Scope { value, lint_level, region_scope } = v.kind else {
+                    span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
+                };
+
+                let v = &this.thir[value];
+                let ExprKind::Call { ref args, fun, fn_span, .. } = v.kind else {
+                    span_bug!(v.span, "`thir_check_tail_calls` should have disallowed this {v:?}")
+                };
+
+                this.in_scope((region_scope, source_info), lint_level, |this| {
+                    let fun = unpack!(block = this.as_local_operand(block, fun));
+                    let args: Box<[_]> = args
+                        .into_iter()
+                        .copied()
+                        .map(|arg| Spanned {
+                            node: unpack!(block = this.as_local_call_operand(block, arg)),
+                            span: this.thir.exprs[arg].span,
+                        })
+                        .collect();
+
+                    this.record_operands_moved(&args);
+
+                    debug!("expr_into_dest: fn_span={:?}", fn_span);
+
+                    unpack!(block = this.break_for_tail_call(block, &args, source_info));
+
+                    this.cfg.terminate(
+                        block,
+                        source_info,
+                        TerminatorKind::TailCall { func: fun, args, fn_span },
+                    );
+
+                    this.cfg.start_new_block().unit()
+                })
             }
             _ => {
                 assert!(
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 932406fd1aa..5695c881ecc 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -21,12 +21,14 @@ use rustc_span::symbol::Symbol;
 use rustc_span::{BytePos, Pos, Span};
 use rustc_target::abi::VariantIdx;
 use tracing::{debug, instrument};
+use util::visit_bindings;
 
 // helper functions, broken out by category:
 mod simplify;
 mod test;
 mod util;
 
+use std::assert_matches::assert_matches;
 use std::borrow::Borrow;
 use std::mem;
 
@@ -38,9 +40,50 @@ struct ThenElseArgs {
     /// `self.local_scope()` is used.
     temp_scope_override: Option<region::Scope>,
     variable_source_info: SourceInfo,
+    /// Determines how bindings should be handled when lowering `let` expressions.
+    ///
     /// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
-    /// When false (for match guards), `let` bindings won't be declared.
-    declare_let_bindings: bool,
+    declare_let_bindings: DeclareLetBindings,
+}
+
+/// Should lowering a `let` expression also declare its bindings?
+///
+/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
+#[derive(Clone, Copy)]
+pub(crate) enum DeclareLetBindings {
+    /// Yes, declare `let` bindings as normal for `if` conditions.
+    Yes,
+    /// No, don't declare `let` bindings, because the caller declares them
+    /// separately due to special requirements.
+    ///
+    /// Used for match guards and let-else.
+    No,
+    /// Let expressions are not permitted in this context, so it is a bug to
+    /// try to lower one (e.g inside lazy-boolean-or or boolean-not).
+    LetNotPermitted,
+}
+
+/// Used by [`Builder::bind_matched_candidate_for_arm_body`] to determine
+/// whether or not to call [`Builder::storage_live_binding`] to emit
+/// [`StatementKind::StorageLive`].
+#[derive(Clone, Copy)]
+pub(crate) enum EmitStorageLive {
+    /// Yes, emit `StorageLive` as normal.
+    Yes,
+    /// No, don't emit `StorageLive`. The caller has taken responsibility for
+    /// emitting `StorageLive` as appropriate.
+    No,
+}
+
+/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`]
+/// to decide whether to schedule drops.
+#[derive(Clone, Copy, Debug)]
+pub(crate) enum ScheduleDrops {
+    /// Yes, the relevant functions should also schedule drops as appropriate.
+    Yes,
+    /// No, don't schedule drops. The caller has taken responsibility for any
+    /// appropriate drops.
+    No,
 }
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
@@ -56,7 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         expr_id: ExprId,
         temp_scope_override: Option<region::Scope>,
         variable_source_info: SourceInfo,
-        declare_let_bindings: bool,
+        declare_let_bindings: DeclareLetBindings,
     ) -> BlockAnd<()> {
         self.then_else_break_inner(
             block,
@@ -90,13 +133,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         this.then_else_break_inner(
                             block,
                             lhs,
-                            ThenElseArgs { declare_let_bindings: true, ..args },
+                            ThenElseArgs {
+                                declare_let_bindings: DeclareLetBindings::LetNotPermitted,
+                                ..args
+                            },
                         )
                     });
                 let rhs_success_block = unpack!(this.then_else_break_inner(
                     failure_block,
                     rhs,
-                    ThenElseArgs { declare_let_bindings: true, ..args },
+                    ThenElseArgs {
+                        declare_let_bindings: DeclareLetBindings::LetNotPermitted,
+                        ..args
+                    },
                 ));
 
                 // Make the LHS and RHS success arms converge to a common block.
@@ -111,8 +160,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // Improve branch coverage instrumentation by noting conditions
                 // nested within one or more `!` expressions.
                 // (Skipped if branch coverage is not enabled.)
-                if let Some(branch_info) = this.coverage_branch_info.as_mut() {
-                    branch_info.visit_unary_not(this.thir, expr_id);
+                if let Some(coverage_info) = this.coverage_info.as_mut() {
+                    coverage_info.visit_unary_not(this.thir, expr_id);
                 }
 
                 let local_scope = this.local_scope();
@@ -126,7 +175,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         this.then_else_break_inner(
                             block,
                             arg,
-                            ThenElseArgs { declare_let_bindings: true, ..args },
+                            ThenElseArgs {
+                                declare_let_bindings: DeclareLetBindings::LetNotPermitted,
+                                ..args
+                            },
                         )
                     });
                 this.break_for_else(success_block, args.variable_source_info);
@@ -146,6 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 Some(args.variable_source_info.scope),
                 args.variable_source_info.span,
                 args.declare_let_bindings,
+                EmitStorageLive::Yes,
             ),
             _ => {
                 let mut block = block;
@@ -314,13 +367,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         let match_start_span = span.shrink_to_lo().to(scrutinee_span);
 
-        let fake_borrow_temps = self.lower_match_tree(
+        // The set of places that we are creating fake borrows of. If there are no match guards then
+        // we don't need any fake borrows, so don't track them.
+        let fake_borrow_temps: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
+            util::collect_fake_borrows(self, &candidates, scrutinee_span, scrutinee_place.base())
+        } else {
+            Vec::new()
+        };
+
+        self.lower_match_tree(
             block,
             scrutinee_span,
             &scrutinee_place,
             match_start_span,
-            match_has_guard,
             &mut candidates,
+            false,
         );
 
         self.lower_match_arms(
@@ -375,89 +436,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             .collect()
     }
 
-    /// Create the decision tree for the match expression, starting from `block`.
-    ///
-    /// Modifies `candidates` to store the bindings and type ascriptions for
-    /// that candidate.
-    ///
-    /// Returns the places that need fake borrows because we bind or test them.
-    fn lower_match_tree<'pat>(
-        &mut self,
-        block: BasicBlock,
-        scrutinee_span: Span,
-        scrutinee_place_builder: &PlaceBuilder<'tcx>,
-        match_start_span: Span,
-        match_has_guard: bool,
-        candidates: &mut [&mut Candidate<'pat, 'tcx>],
-    ) -> Vec<(Place<'tcx>, Local, FakeBorrowKind)> {
-        // The set of places that we are creating fake borrows of. If there are no match guards then
-        // we don't need any fake borrows, so don't track them.
-        let fake_borrows: Vec<(Place<'tcx>, Local, FakeBorrowKind)> = if match_has_guard {
-            util::collect_fake_borrows(
-                self,
-                candidates,
-                scrutinee_span,
-                scrutinee_place_builder.base(),
-            )
-        } else {
-            Vec::new()
-        };
-
-        // See the doc comment on `match_candidates` for why we have an
-        // otherwise block. Match checking will ensure this is actually
-        // unreachable.
-        let otherwise_block = self.cfg.start_new_block();
-
-        // This will generate code to test scrutinee_place and
-        // branch to the appropriate arm block
-        self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
-
-        let source_info = self.source_info(scrutinee_span);
-
-        // Matching on a `scrutinee_place` with an uninhabited type doesn't
-        // generate any memory reads by itself, and so if the place "expression"
-        // contains unsafe operations like raw pointer dereferences or union
-        // field projections, we wouldn't know to require an `unsafe` block
-        // around a `match` equivalent to `std::intrinsics::unreachable()`.
-        // See issue #47412 for this hole being discovered in the wild.
-        //
-        // HACK(eddyb) Work around the above issue by adding a dummy inspection
-        // of `scrutinee_place`, specifically by applying `ReadForMatch`.
-        //
-        // NOTE: ReadForMatch also checks that the scrutinee is initialized.
-        // This is currently needed to not allow matching on an uninitialized,
-        // uninhabited value. If we get never patterns, those will check that
-        // the place is initialized, and so this read would only be used to
-        // check safety.
-        let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
-
-        if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
-            self.cfg.push_fake_read(
-                otherwise_block,
-                source_info,
-                cause_matched_place,
-                scrutinee_place,
-            );
-        }
-
-        self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
-
-        // Link each leaf candidate to the `pre_binding_block` of the next one.
-        let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
-
-        for candidate in candidates {
-            candidate.visit_leaves(|leaf_candidate| {
-                if let Some(ref mut prev) = previous_candidate {
-                    assert!(leaf_candidate.false_edge_start_block.is_some());
-                    prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
-                }
-                previous_candidate = Some(leaf_candidate);
-            });
-        }
-
-        fake_borrows
-    }
-
     /// Lower the bindings, guards and arm bodies of a `match` expression.
     ///
     /// The decision tree should have already been created
@@ -513,7 +491,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         &fake_borrow_temps,
                         scrutinee_span,
                         Some((arm, match_scope)),
-                        false,
+                        EmitStorageLive::Yes,
                     );
 
                     this.fixed_temps_scope = old_dedup_scope;
@@ -558,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
         scrutinee_span: Span,
         arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
-        storages_alive: bool,
+        emit_storage_live: EmitStorageLive,
     ) -> BasicBlock {
         if candidate.subcandidates.is_empty() {
             // Avoid generating another `BasicBlock` when we only have one
@@ -569,8 +547,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 fake_borrow_temps,
                 scrutinee_span,
                 arm_match_scope,
-                true,
-                storages_alive,
+                ScheduleDrops::Yes,
+                emit_storage_live,
             )
         } else {
             // It's helpful to avoid scheduling drops multiple times to save
@@ -588,7 +566,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // To handle this we instead unschedule it's drop after each time
             // we lower the guard.
             let target_block = self.cfg.start_new_block();
-            let mut schedule_drops = true;
+            let mut schedule_drops = ScheduleDrops::Yes;
             let arm = arm_match_scope.unzip().0;
             // We keep a stack of all of the bindings and type ascriptions
             // from the parent candidates that we visit, that also need to
@@ -607,10 +585,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         scrutinee_span,
                         arm_match_scope,
                         schedule_drops,
-                        storages_alive,
+                        emit_storage_live,
                     );
                     if arm.is_none() {
-                        schedule_drops = false;
+                        schedule_drops = ScheduleDrops::No;
                     }
                     self.cfg.goto(binding_end, outer_source_info, target_block);
                 },
@@ -636,8 +614,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         match irrefutable_pat.kind {
             // Optimize the case of `let x = ...` to write directly into `x`
             PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
-                let place =
-                    self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
+                let place = self.storage_live_binding(
+                    block,
+                    var,
+                    irrefutable_pat.span,
+                    OutsideGuard,
+                    ScheduleDrops::Yes,
+                );
                 unpack!(block = self.expr_into_dest(place, block, initializer_id));
 
                 // Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -670,8 +653,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     },
                 ascription: thir::Ascription { ref annotation, variance: _ },
             } => {
-                let place =
-                    self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
+                let place = self.storage_live_binding(
+                    block,
+                    var,
+                    irrefutable_pat.span,
+                    OutsideGuard,
+                    ScheduleDrops::Yes,
+                );
                 unpack!(block = self.expr_into_dest(place, block, initializer_id));
 
                 // Inject a fake read, see comments on `FakeReadCause::ForLet`.
@@ -728,62 +716,56 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         set_match_place: bool,
     ) -> BlockAnd<()> {
         let mut candidate = Candidate::new(initializer.clone(), irrefutable_pat, false, self);
-        let fake_borrow_temps = self.lower_match_tree(
-            block,
-            irrefutable_pat.span,
-            &initializer,
-            irrefutable_pat.span,
-            false,
-            &mut [&mut candidate],
-        );
 
         // For matches and function arguments, the place that is being matched
         // can be set when creating the variables. But the place for
         // let PATTERN = ... might not even exist until we do the assignment.
         // so we set it here instead.
         if set_match_place {
-            let mut next = Some(&candidate);
-            while let Some(candidate_ref) = next.take() {
-                for binding in &candidate_ref.extra_data.bindings {
+            // `try_to_place` may fail if it is unable to resolve the given `PlaceBuilder` inside a
+            // closure. In this case, we don't want to include a scrutinee place.
+            // `scrutinee_place_builder` will fail for destructured assignments. This is because a
+            // closure only captures the precise places that it will read and as a result a closure
+            // may not capture the entire tuple/struct and rather have individual places that will
+            // be read in the final MIR.
+            // Example:
+            // ```
+            // let foo = (0, 1);
+            // let c = || {
+            //    let (v1, v2) = foo;
+            // };
+            // ```
+            if let Some(place) = initializer.try_to_place(self) {
+                visit_bindings(&[&mut candidate], |binding: &Binding<'_>| {
                     let local = self.var_local_id(binding.var_id, OutsideGuard);
-                    // `try_to_place` may fail if it is unable to resolve the given
-                    // `PlaceBuilder` inside a closure. In this case, we don't want to include
-                    // a scrutinee place. `scrutinee_place_builder` will fail for destructured
-                    // assignments. This is because a closure only captures the precise places
-                    // that it will read and as a result a closure may not capture the entire
-                    // tuple/struct and rather have individual places that will be read in the
-                    // final MIR.
-                    // Example:
-                    // ```
-                    // let foo = (0, 1);
-                    // let c = || {
-                    //    let (v1, v2) = foo;
-                    // };
-                    // ```
-                    if let Some(place) = initializer.try_to_place(self) {
-                        let LocalInfo::User(BindingForm::Var(VarBindingForm {
-                            opt_match_place: Some((ref mut match_place, _)),
-                            ..
-                        })) = **self.local_decls[local].local_info.as_mut().assert_crate_local()
-                        else {
-                            bug!("Let binding to non-user variable.")
-                        };
+                    if let LocalInfo::User(BindingForm::Var(VarBindingForm {
+                        opt_match_place: Some((ref mut match_place, _)),
+                        ..
+                    })) = **self.local_decls[local].local_info.as_mut().assert_crate_local()
+                    {
                         *match_place = Some(place);
-                    }
-                }
-                // All of the subcandidates should bind the same locals, so we
-                // only visit the first one.
-                next = candidate_ref.subcandidates.get(0)
+                    } else {
+                        bug!("Let binding to non-user variable.")
+                    };
+                });
             }
         }
 
+        self.lower_match_tree(
+            block,
+            irrefutable_pat.span,
+            &initializer,
+            irrefutable_pat.span,
+            &mut [&mut candidate],
+            false,
+        );
         self.bind_pattern(
             self.source_info(irrefutable_pat.span),
             candidate,
-            fake_borrow_temps.as_slice(),
+            &[],
             irrefutable_pat.span,
             None,
-            false,
+            EmitStorageLive::Yes,
         )
         .unit()
     }
@@ -859,13 +841,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         }
     }
 
+    /// Emits a [`StatementKind::StorageLive`] for the given var, and also
+    /// schedules a drop if requested (and possible).
     pub(crate) fn storage_live_binding(
         &mut self,
         block: BasicBlock,
         var: LocalVarId,
         span: Span,
         for_guard: ForGuard,
-        schedule_drop: bool,
+        schedule_drop: ScheduleDrops,
     ) -> Place<'tcx> {
         let local_id = self.var_local_id(var, for_guard);
         let source_info = self.source_info(span);
@@ -873,7 +857,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // Although there is almost always scope for given variable in corner cases
         // like #92893 we might get variable with no scope.
         if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)
-            && schedule_drop
+            && matches!(schedule_drop, ScheduleDrops::Yes)
         {
             self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
         }
@@ -1306,6 +1290,79 @@ pub(crate) struct ArmHasGuard(pub(crate) bool);
 // Main matching algorithm
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
+    /// The entrypoint of the matching algorithm. Create the decision tree for the match expression,
+    /// starting from `block`.
+    ///
+    /// Modifies `candidates` to store the bindings and type ascriptions for
+    /// that candidate.
+    ///
+    /// `refutable` indicates whether the candidate list is refutable (for `if let` and `let else`)
+    /// or not (for `let` and `match`). In the refutable case we return the block to which we branch
+    /// on failure.
+    fn lower_match_tree<'pat>(
+        &mut self,
+        block: BasicBlock,
+        scrutinee_span: Span,
+        scrutinee_place_builder: &PlaceBuilder<'tcx>,
+        match_start_span: Span,
+        candidates: &mut [&mut Candidate<'pat, 'tcx>],
+        refutable: bool,
+    ) -> BasicBlock {
+        // See the doc comment on `match_candidates` for why we have an otherwise block.
+        let otherwise_block = self.cfg.start_new_block();
+
+        // This will generate code to test scrutinee_place and branch to the appropriate arm block
+        self.match_candidates(match_start_span, scrutinee_span, block, otherwise_block, candidates);
+
+        // Link each leaf candidate to the `false_edge_start_block` of the next one.
+        let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
+        for candidate in candidates {
+            candidate.visit_leaves(|leaf_candidate| {
+                if let Some(ref mut prev) = previous_candidate {
+                    assert!(leaf_candidate.false_edge_start_block.is_some());
+                    prev.next_candidate_start_block = leaf_candidate.false_edge_start_block;
+                }
+                previous_candidate = Some(leaf_candidate);
+            });
+        }
+
+        if refutable {
+            // In refutable cases there's always at least one candidate, and we want a false edge to
+            // the failure block.
+            previous_candidate.as_mut().unwrap().next_candidate_start_block = Some(otherwise_block)
+        } else {
+            // Match checking ensures `otherwise_block` is actually unreachable in irrefutable
+            // cases.
+            let source_info = self.source_info(scrutinee_span);
+
+            // Matching on a scrutinee place of an uninhabited type doesn't generate any memory
+            // reads by itself, and so if the place is uninitialized we wouldn't know. In order to
+            // disallow the following:
+            // ```rust
+            // let x: !;
+            // match x {}
+            // ```
+            // we add a dummy read on the place.
+            //
+            // NOTE: If we require never patterns for empty matches, those will check that the place
+            // is initialized, and so this read would no longer be needed.
+            let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
+
+            if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
+                self.cfg.push_fake_read(
+                    otherwise_block,
+                    source_info,
+                    cause_matched_place,
+                    scrutinee_place,
+                );
+            }
+
+            self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
+        }
+
+        otherwise_block
+    }
+
     /// The main match algorithm. It begins with a set of candidates
     /// `candidates` and has the job of generating code to determine
     /// which of these candidates, if any, is the correct one. The
@@ -1411,6 +1468,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     break;
                 }
             }
+            if expand_until != 0 {
+                expand_until = i + 1;
+            }
         }
         let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
 
@@ -1997,53 +2057,69 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 // Pat binding - used for `let` and function parameters as well.
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
-    /// If the bindings have already been declared, set `declare_bindings` to
-    /// `false` to avoid duplicated bindings declaration. Used for if-let guards.
+    /// Lowers a `let` expression that appears in a suitable context
+    /// (e.g. an `if` condition or match guard).
+    ///
+    /// Also used for lowering let-else statements, since they have similar
+    /// needs despite not actually using `let` expressions.
+    ///
+    /// Use [`DeclareLetBindings`] to control whether the `let` bindings are
+    /// declared or not.
     pub(crate) fn lower_let_expr(
         &mut self,
         mut block: BasicBlock,
         expr_id: ExprId,
         pat: &Pat<'tcx>,
         source_scope: Option<SourceScope>,
-        span: Span,
-        declare_bindings: bool,
+        scope_span: Span,
+        declare_let_bindings: DeclareLetBindings,
+        emit_storage_live: EmitStorageLive,
     ) -> BlockAnd<()> {
         let expr_span = self.thir[expr_id].span;
-        let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
-        let wildcard = Pat::wildcard_from_ty(pat.ty);
-        let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self);
-        let mut otherwise_candidate =
-            Candidate::new(expr_place_builder.clone(), &wildcard, false, self);
-        let fake_borrow_temps = self.lower_match_tree(
+        let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
+        let mut candidate = Candidate::new(scrutinee.clone(), pat, false, self);
+        let otherwise_block = self.lower_match_tree(
             block,
+            expr_span,
+            &scrutinee,
             pat.span,
-            &expr_place_builder,
-            pat.span,
-            false,
-            &mut [&mut guard_candidate, &mut otherwise_candidate],
+            &mut [&mut candidate],
+            true,
         );
-        let expr_place = expr_place_builder.try_to_place(self);
-        let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
-        let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
-        self.break_for_else(otherwise_post_guard_block, self.source_info(expr_span));
 
-        if declare_bindings {
-            self.declare_bindings(source_scope, pat.span.to(span), pat, None, opt_expr_place);
+        self.break_for_else(otherwise_block, self.source_info(expr_span));
+
+        match declare_let_bindings {
+            DeclareLetBindings::Yes => {
+                let expr_place = scrutinee.try_to_place(self);
+                let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
+                self.declare_bindings(
+                    source_scope,
+                    pat.span.to(scope_span),
+                    pat,
+                    None,
+                    opt_expr_place,
+                );
+            }
+            DeclareLetBindings::No => {} // Caller is responsible for bindings.
+            DeclareLetBindings::LetNotPermitted => {
+                self.tcx.dcx().span_bug(expr_span, "let expression not expected in this context")
+            }
         }
 
-        let post_guard_block = self.bind_pattern(
+        let success = self.bind_pattern(
             self.source_info(pat.span),
-            guard_candidate,
-            fake_borrow_temps.as_slice(),
+            candidate,
+            &[],
             expr_span,
             None,
-            false,
+            emit_storage_live,
         );
 
         // If branch coverage is enabled, record this branch.
-        self.visit_coverage_conditional_let(pat, post_guard_block, otherwise_post_guard_block);
+        self.visit_coverage_conditional_let(pat, success, otherwise_block);
 
-        post_guard_block.unit()
+        success.unit()
     }
 
     /// Initializes each of the bindings from the candidate by
@@ -2061,8 +2137,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
         scrutinee_span: Span,
         arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
-        schedule_drops: bool,
-        storages_alive: bool,
+        schedule_drops: ScheduleDrops,
+        emit_storage_live: EmitStorageLive,
     ) -> BasicBlock {
         debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
 
@@ -2091,14 +2167,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             return self.cfg.start_new_block();
         }
 
-        self.ascribe_types(
-            block,
-            parent_data
-                .iter()
-                .flat_map(|d| &d.ascriptions)
-                .cloned()
-                .chain(candidate.extra_data.ascriptions),
-        );
+        let ascriptions = parent_data
+            .iter()
+            .flat_map(|d| &d.ascriptions)
+            .cloned()
+            .chain(candidate.extra_data.ascriptions);
+        let bindings =
+            parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
+
+        self.ascribe_types(block, ascriptions);
 
         // rust-lang/rust#27282: The `autoref` business deserves some
         // explanation here.
@@ -2185,12 +2262,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             && let Some(guard) = arm.guard
         {
             let tcx = self.tcx;
-            let bindings =
-                parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings);
 
             self.bind_matched_candidate_for_guard(block, schedule_drops, bindings.clone());
-            let guard_frame =
-                GuardFrame { locals: bindings.map(|b| GuardFrameLocal::new(b.var_id)).collect() };
+            let guard_frame = GuardFrame {
+                locals: bindings.clone().map(|b| GuardFrameLocal::new(b.var_id)).collect(),
+            };
             debug!("entering guard building context: {:?}", guard_frame);
             self.guard_context.push(guard_frame);
 
@@ -2211,7 +2287,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         guard,
                         None, // Use `self.local_scope()` as the temp scope
                         this.source_info(arm.span),
-                        false, // For guards, `let` bindings are declared separately
+                        DeclareLetBindings::No, // For guards, `let` bindings are declared separately
                     )
                 });
 
@@ -2263,11 +2339,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // ```
             //
             // and that is clearly not correct.
-            let by_value_bindings = parent_data
-                .iter()
-                .flat_map(|d| &d.bindings)
-                .chain(&candidate.extra_data.bindings)
-                .filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
+            let by_value_bindings =
+                bindings.filter(|binding| matches!(binding.binding_mode.0, ByRef::No));
             // Read all of the by reference bindings to ensure that the
             // place they refer to can't be modified by the guard.
             for binding in by_value_bindings.clone() {
@@ -2275,12 +2348,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let cause = FakeReadCause::ForGuardBinding;
                 self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
             }
-            assert!(schedule_drops, "patterns with guards must schedule drops");
+            assert_matches!(
+                schedule_drops,
+                ScheduleDrops::Yes,
+                "patterns with guards must schedule drops"
+            );
             self.bind_matched_candidate_for_arm_body(
                 post_guard_block,
-                true,
+                ScheduleDrops::Yes,
                 by_value_bindings,
-                storages_alive,
+                emit_storage_live,
             );
 
             post_guard_block
@@ -2291,8 +2368,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             self.bind_matched_candidate_for_arm_body(
                 block,
                 schedule_drops,
-                parent_data.iter().flat_map(|d| &d.bindings).chain(&candidate.extra_data.bindings),
-                storages_alive,
+                bindings,
+                emit_storage_live,
             );
             block
         }
@@ -2328,7 +2405,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     fn bind_matched_candidate_for_guard<'b>(
         &mut self,
         block: BasicBlock,
-        schedule_drops: bool,
+        schedule_drops: ScheduleDrops,
         bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
     ) where
         'tcx: 'b,
@@ -2381,9 +2458,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     fn bind_matched_candidate_for_arm_body<'b>(
         &mut self,
         block: BasicBlock,
-        schedule_drops: bool,
+        schedule_drops: ScheduleDrops,
         bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
-        storages_alive: bool,
+        emit_storage_live: EmitStorageLive,
     ) where
         'tcx: 'b,
     {
@@ -2393,21 +2470,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // Assign each of the bindings. This may trigger moves out of the candidate.
         for binding in bindings {
             let source_info = self.source_info(binding.span);
-            let local = if storages_alive {
+            let local = match emit_storage_live {
                 // Here storages are already alive, probably because this is a binding
                 // from let-else.
                 // We just need to schedule drop for the value.
-                self.var_local_id(binding.var_id, OutsideGuard).into()
-            } else {
-                self.storage_live_binding(
+                EmitStorageLive::No => self.var_local_id(binding.var_id, OutsideGuard).into(),
+                EmitStorageLive::Yes => self.storage_live_binding(
                     block,
                     binding.var_id,
                     binding.span,
                     OutsideGuard,
                     schedule_drops,
-                )
+                ),
             };
-            if schedule_drops {
+            if matches!(schedule_drops, ScheduleDrops::Yes) {
                 self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
             }
             let rvalue = match binding.binding_mode.0 {
@@ -2493,55 +2569,4 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!(?locals);
         self.var_indices.insert(var_id, locals);
     }
-
-    pub(crate) fn ast_let_else(
-        &mut self,
-        mut block: BasicBlock,
-        init_id: ExprId,
-        initializer_span: Span,
-        else_block: BlockId,
-        let_else_scope: &region::Scope,
-        pattern: &Pat<'tcx>,
-    ) -> BlockAnd<BasicBlock> {
-        let else_block_span = self.thir[else_block].span;
-        let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| {
-            let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span));
-            let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild };
-            let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
-            let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
-            let fake_borrow_temps = this.lower_match_tree(
-                block,
-                initializer_span,
-                &scrutinee,
-                pattern.span,
-                false,
-                &mut [&mut candidate, &mut wildcard],
-            );
-            // This block is for the matching case
-            let matching = this.bind_pattern(
-                this.source_info(pattern.span),
-                candidate,
-                fake_borrow_temps.as_slice(),
-                initializer_span,
-                None,
-                true,
-            );
-            // This block is for the failure case
-            let failure = this.bind_pattern(
-                this.source_info(else_block_span),
-                wildcard,
-                fake_borrow_temps.as_slice(),
-                initializer_span,
-                None,
-                true,
-            );
-
-            // If branch coverage is enabled, record this branch.
-            this.visit_coverage_conditional_let(pattern, matching, failure);
-
-            this.break_for_else(failure, this.source_info(initializer_span));
-            matching.unit()
-        });
-        matching.and(failure)
-    }
 }
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
index 630d0b9438d..3bec154e1df 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -1,3 +1,5 @@
+use std::marker::PhantomData;
+
 use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
 use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
 use crate::build::Builder;
@@ -269,18 +271,6 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
     }
 }
 
-pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
-    cx: &'a mut Builder<'b, 'tcx>,
-    /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
-    /// bindings inside deref patterns.
-    scrutinee_base: PlaceBase,
-    /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
-    /// borrow (i.e. Deep > Shallow).
-    /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
-    /// dereferences are also borrowed with the same of stronger borrow kind.
-    fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
-}
-
 /// Determine the set of places that have to be stable across match guards.
 ///
 /// Returns a list of places that need a fake borrow along with a local to store it.
@@ -344,6 +334,18 @@ pub(super) fn collect_fake_borrows<'tcx>(
         .collect()
 }
 
+pub(super) struct FakeBorrowCollector<'a, 'b, 'tcx> {
+    cx: &'a mut Builder<'b, 'tcx>,
+    /// Base of the scrutinee place. Used to distinguish bindings inside the scrutinee place from
+    /// bindings inside deref patterns.
+    scrutinee_base: PlaceBase,
+    /// Store for each place the kind of borrow to take. In case of conflicts, we take the strongest
+    /// borrow (i.e. Deep > Shallow).
+    /// Invariant: for any place in `fake_borrows`, all the prefixes of this place that are
+    /// dereferences are also borrowed with the same of stronger borrow kind.
+    fake_borrows: FxIndexMap<Place<'tcx>, FakeBorrowKind>,
+}
+
 impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
     // Fake borrow this place and its dereference prefixes.
     fn fake_borrow(&mut self, place: Place<'tcx>, kind: FakeBorrowKind) {
@@ -457,6 +459,57 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
     }
 }
 
+/// Visit all the bindings of these candidates. Because or-alternatives bind the same variables, we
+/// only explore the first one of each or-pattern.
+pub(super) fn visit_bindings<'tcx>(
+    candidates: &[&mut Candidate<'_, 'tcx>],
+    f: impl FnMut(&Binding<'tcx>),
+) {
+    let mut visitor = BindingsVisitor { f, phantom: PhantomData };
+    for candidate in candidates.iter() {
+        visitor.visit_candidate(candidate);
+    }
+}
+
+pub(super) struct BindingsVisitor<'tcx, F> {
+    f: F,
+    phantom: PhantomData<&'tcx ()>,
+}
+
+impl<'tcx, F> BindingsVisitor<'tcx, F>
+where
+    F: FnMut(&Binding<'tcx>),
+{
+    fn visit_candidate(&mut self, candidate: &Candidate<'_, 'tcx>) {
+        for binding in &candidate.extra_data.bindings {
+            (self.f)(binding)
+        }
+        for match_pair in &candidate.match_pairs {
+            self.visit_match_pair(match_pair);
+        }
+    }
+
+    fn visit_flat_pat(&mut self, flat_pat: &FlatPat<'_, 'tcx>) {
+        for binding in &flat_pat.extra_data.bindings {
+            (self.f)(binding)
+        }
+        for match_pair in &flat_pat.match_pairs {
+            self.visit_match_pair(match_pair);
+        }
+    }
+
+    fn visit_match_pair(&mut self, match_pair: &MatchPair<'_, 'tcx>) {
+        if let TestCase::Or { pats, .. } = &match_pair.test_case {
+            // All the or-alternatives should bind the same locals, so we only visit the first one.
+            self.visit_flat_pat(&pats[0])
+        } else {
+            for subpair in &match_pair.subpairs {
+                self.visit_match_pair(subpair);
+            }
+        }
+    }
+}
+
 #[must_use]
 pub(crate) fn ref_pat_borrow_kind(ref_mutability: Mutability) -> BorrowKind {
     match ref_mutability {
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index 601e5d4d3dc..0f9746cb719 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -218,8 +218,8 @@ struct Builder<'a, 'tcx> {
     lint_level_roots_cache: GrowableBitSet<hir::ItemLocalId>,
 
     /// Collects additional coverage information during MIR building.
-    /// Only present if branch coverage is enabled and this function is eligible.
-    coverage_branch_info: Option<coverageinfo::BranchInfoBuilder>,
+    /// Only present if coverage is enabled and this function is eligible.
+    coverage_info: Option<coverageinfo::CoverageInfoBuilder>,
 }
 
 type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>;
@@ -773,7 +773,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             unit_temp: None,
             var_debug_info: vec![],
             lint_level_roots_cache: GrowableBitSet::new_empty(),
-            coverage_branch_info: coverageinfo::BranchInfoBuilder::new_if_enabled(tcx, def),
+            coverage_info: coverageinfo::CoverageInfoBuilder::new_if_enabled(tcx, def),
         };
 
         assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
@@ -802,7 +802,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             self.coroutine,
             None,
         );
-        body.coverage_branch_info = self.coverage_branch_info.and_then(|b| b.into_done());
+        body.coverage_info_hi = self.coverage_info.map(|b| b.into_done());
         body
     }
 
diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs
index 5b6de39bb2e..948301e2ece 100644
--- a/compiler/rustc_mir_build/src/build/scope.rs
+++ b/compiler/rustc_mir_build/src/build/scope.rs
@@ -745,6 +745,91 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         self.cfg.terminate(block, source_info, TerminatorKind::UnwindResume);
     }
 
+    /// Sets up the drops for explict tail calls.
+    ///
+    /// Unlike other kinds of early exits, tail calls do not go through the drop tree.
+    /// Instead, all scheduled drops are immediately added to the CFG.
+    pub(crate) fn break_for_tail_call(
+        &mut self,
+        mut block: BasicBlock,
+        args: &[Spanned<Operand<'tcx>>],
+        source_info: SourceInfo,
+    ) -> BlockAnd<()> {
+        let arg_drops: Vec<_> = args
+            .iter()
+            .rev()
+            .filter_map(|arg| match &arg.node {
+                Operand::Copy(_) => bug!("copy op in tail call args"),
+                Operand::Move(place) => {
+                    let local =
+                        place.as_local().unwrap_or_else(|| bug!("projection in tail call args"));
+
+                    Some(DropData { source_info, local, kind: DropKind::Value })
+                }
+                Operand::Constant(_) => None,
+            })
+            .collect();
+
+        let mut unwind_to = self.diverge_cleanup_target(
+            self.scopes.scopes.iter().rev().nth(1).unwrap().region_scope,
+            DUMMY_SP,
+        );
+        let unwind_drops = &mut self.scopes.unwind_drops;
+
+        // the innermost scope contains only the destructors for the tail call arguments
+        // we only want to drop these in case of a panic, so we skip it
+        for scope in self.scopes.scopes[1..].iter().rev().skip(1) {
+            // FIXME(explicit_tail_calls) code duplication with `build_scope_drops`
+            for drop_data in scope.drops.iter().rev() {
+                let source_info = drop_data.source_info;
+                let local = drop_data.local;
+
+                match drop_data.kind {
+                    DropKind::Value => {
+                        // `unwind_to` should drop the value that we're about to
+                        // schedule. If dropping this value panics, then we continue
+                        // with the *next* value on the unwind path.
+                        debug_assert_eq!(unwind_drops.drops[unwind_to].data.local, drop_data.local);
+                        debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind);
+                        unwind_to = unwind_drops.drops[unwind_to].next;
+
+                        let mut unwind_entry_point = unwind_to;
+
+                        // the tail call arguments must be dropped if any of these drops panic
+                        for drop in arg_drops.iter().copied() {
+                            unwind_entry_point = unwind_drops.add_drop(drop, unwind_entry_point);
+                        }
+
+                        unwind_drops.add_entry_point(block, unwind_entry_point);
+
+                        let next = self.cfg.start_new_block();
+                        self.cfg.terminate(
+                            block,
+                            source_info,
+                            TerminatorKind::Drop {
+                                place: local.into(),
+                                target: next,
+                                unwind: UnwindAction::Continue,
+                                replace: false,
+                            },
+                        );
+                        block = next;
+                    }
+                    DropKind::Storage => {
+                        // Only temps and vars need their storage dead.
+                        assert!(local.index() > self.arg_count);
+                        self.cfg.push(
+                            block,
+                            Statement { source_info, kind: StatementKind::StorageDead(local) },
+                        );
+                    }
+                }
+            }
+        }
+
+        block.unit()
+    }
+
     fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock {
         // If we are emitting a `drop` statement, we need to have the cached
         // diverge cleanup pads ready in case that drop panics.
@@ -1523,6 +1608,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::Yield { .. }
             | TerminatorKind::CoroutineDrop
diff --git a/compiler/rustc_mir_build/src/lints.rs b/compiler/rustc_mir_build/src/lints.rs
index 2c817d605af..263e777d03a 100644
--- a/compiler/rustc_mir_build/src/lints.rs
+++ b/compiler/rustc_mir_build/src/lints.rs
@@ -141,7 +141,7 @@ impl<'tcx> TerminatorClassifier<'tcx> for CallRecursion<'tcx> {
                 return false;
             };
             let (callee, call_args) = if let Ok(Some(instance)) =
-                Instance::resolve(tcx, param_env, callee, normalized_args)
+                Instance::try_resolve(tcx, param_env, callee, normalized_args)
             {
                 (instance.def_id(), instance.args)
             } else {
@@ -217,12 +217,28 @@ impl<'mir, 'tcx, C: TerminatorClassifier<'tcx>> TriColorVisitor<BasicBlocks<'tcx
             | TerminatorKind::FalseUnwind { .. }
             | TerminatorKind::Goto { .. }
             | TerminatorKind::SwitchInt { .. } => ControlFlow::Continue(()),
+
+            // Note that tail call terminator technically returns to the caller,
+            // but for purposes of this lint it makes sense to count it as possibly recursive,
+            // since it's still a call.
+            //
+            // If this'll be repurposed for something else, this might need to be changed.
+            TerminatorKind::TailCall { .. } => ControlFlow::Continue(()),
         }
     }
 
     fn node_settled(&mut self, bb: BasicBlock) -> ControlFlow<Self::BreakVal> {
         // When we examine a node for the last time, remember it if it is a recursive call.
         let terminator = self.body[bb].terminator();
+
+        // FIXME(explicit_tail_calls): highlight tail calls as "recursive call site"
+        //
+        // We don't want to lint functions that recurse only through tail calls
+        // (such as `fn g() { become () }`), so just adding `| TailCall { ... }`
+        // here won't work.
+        //
+        // But at the same time we would like to highlight both calls in a function like
+        // `fn f() { if false { become f() } else { f() } }`, so we need to figure something out.
         if self.classifier.is_recursive_terminator(self.tcx, self.body, terminator) {
             self.reachable_recursive_calls.push(terminator.source_info.span);
         }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 8c6c9e10cdf..5745dc0969c 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -138,7 +138,7 @@ impl<'tcx> ConstToPat<'tcx> {
             // lints, but no errors), double-check that all types in the const implement
             // `PartialEq`. Even if we have a valtree, we may have found something
             // in there with non-structural-equality, meaning we match using `PartialEq`
-            // and we hence have to check that that impl exists.
+            // and we hence have to check if that impl exists.
             // This is all messy but not worth cleaning up: at some point we'll emit
             // a hard error when we don't have a valtree or when we find something in
             // the valtree that is not structural; then this can all be made a lot simpler.
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 93db1f61853..fd778ef78a3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -558,7 +558,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
         let args = self
             .tcx
             .normalize_erasing_regions(param_env_reveal_all, self.typeck_results.node_args(id));
-        let instance = match ty::Instance::resolve(self.tcx, param_env_reveal_all, def_id, args) {
+        let instance = match ty::Instance::try_resolve(self.tcx, param_env_reveal_all, def_id, args)
+        {
             Ok(Some(i)) => i,
             Ok(None) => {
                 // It should be assoc consts if there's no error but we cannot resolve it.
diff --git a/compiler/rustc_mir_dataflow/src/framework/lattice.rs b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
index 1c2b475a43c..23738f7a4a5 100644
--- a/compiler/rustc_mir_dataflow/src/framework/lattice.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/lattice.rs
@@ -76,6 +76,8 @@ pub trait MeetSemiLattice: Eq {
 /// A set that has a "bottom" element, which is less than or equal to any other element.
 pub trait HasBottom {
     const BOTTOM: Self;
+
+    fn is_bottom(&self) -> bool;
 }
 
 /// A set that has a "top" element, which is greater than or equal to any other element.
@@ -114,6 +116,10 @@ impl MeetSemiLattice for bool {
 
 impl HasBottom for bool {
     const BOTTOM: Self = false;
+
+    fn is_bottom(&self) -> bool {
+        !self
+    }
 }
 
 impl HasTop for bool {
@@ -267,6 +273,10 @@ impl<T: Clone + Eq> MeetSemiLattice for FlatSet<T> {
 
 impl<T> HasBottom for FlatSet<T> {
     const BOTTOM: Self = Self::Bottom;
+
+    fn is_bottom(&self) -> bool {
+        matches!(self, Self::Bottom)
+    }
 }
 
 impl<T> HasTop for FlatSet<T> {
@@ -291,6 +301,10 @@ impl<T> MaybeReachable<T> {
 
 impl<T> HasBottom for MaybeReachable<T> {
     const BOTTOM: Self = MaybeReachable::Unreachable;
+
+    fn is_bottom(&self) -> bool {
+        matches!(self, Self::Unreachable)
+    }
 }
 
 impl<T: HasTop> HasTop for MaybeReachable<T> {
diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
index 706bb796349..574da949b0e 100644
--- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
@@ -145,6 +145,7 @@ where
             | TerminatorKind::InlineAsm { .. }
             | TerminatorKind::UnwindResume
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::SwitchInt { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::Yield { .. } => {}
diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
index f0b79dab0c9..ffcf630b653 100644
--- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs
@@ -50,15 +50,19 @@ use crate::{lattice, AnalysisDomain, GenKill, GenKillAnalysis, MaybeReachable};
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeUninitializedPlaces` yields the set of
 /// places that would require a dynamic drop-flag at that statement.
-pub struct MaybeInitializedPlaces<'a, 'tcx> {
+pub struct MaybeInitializedPlaces<'a, 'mir, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
+    body: &'mir Body<'tcx>,
     mdpe: &'a MoveDataParamEnv<'tcx>,
     skip_unreachable_unwind: bool,
 }
 
-impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        body: &'mir Body<'tcx>,
+        mdpe: &'a MoveDataParamEnv<'tcx>,
+    ) -> Self {
         MaybeInitializedPlaces { tcx, body, mdpe, skip_unreachable_unwind: false }
     }
 
@@ -84,7 +88,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
+impl<'a, 'mir, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'mir, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> {
         &self.mdpe.move_data
     }
@@ -125,17 +129,21 @@ impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
 /// Similarly, at a given `drop` statement, the set-intersection
 /// between this data and `MaybeInitializedPlaces` yields the set of
 /// places that would require a dynamic drop-flag at that statement.
-pub struct MaybeUninitializedPlaces<'a, 'tcx> {
+pub struct MaybeUninitializedPlaces<'a, 'mir, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
+    body: &'mir Body<'tcx>,
     mdpe: &'a MoveDataParamEnv<'tcx>,
 
     mark_inactive_variants_as_uninit: bool,
     skip_unreachable_unwind: BitSet<mir::BasicBlock>,
 }
 
-impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
-    pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+impl<'a, 'mir, 'tcx> MaybeUninitializedPlaces<'a, 'mir, 'tcx> {
+    pub fn new(
+        tcx: TyCtxt<'tcx>,
+        body: &'mir Body<'tcx>,
+        mdpe: &'a MoveDataParamEnv<'tcx>,
+    ) -> Self {
         MaybeUninitializedPlaces {
             tcx,
             body,
@@ -164,7 +172,7 @@ impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
+impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, '_, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> {
         &self.mdpe.move_data
     }
@@ -250,24 +258,24 @@ impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
 ///     c = S;                                  // {a, b, c, d }
 /// }
 /// ```
-pub struct EverInitializedPlaces<'a, 'tcx> {
-    body: &'a Body<'tcx>,
+pub struct EverInitializedPlaces<'a, 'mir, 'tcx> {
+    body: &'mir Body<'tcx>,
     mdpe: &'a MoveDataParamEnv<'tcx>,
 }
 
-impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
-    pub fn new(body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
+impl<'a, 'mir, 'tcx> EverInitializedPlaces<'a, 'mir, 'tcx> {
+    pub fn new(body: &'mir Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
         EverInitializedPlaces { body, mdpe }
     }
 }
 
-impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
+impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, '_, 'tcx> {
     fn move_data(&self) -> &MoveData<'tcx> {
         &self.mdpe.move_data
     }
 }
 
-impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
+impl<'a, 'mir, 'tcx> MaybeInitializedPlaces<'a, 'mir, 'tcx> {
     fn update_bits(
         trans: &mut impl GenKill<MovePathIndex>,
         path: MovePathIndex,
@@ -280,7 +288,7 @@ impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
     }
 }
 
-impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
+impl<'a, 'tcx> MaybeUninitializedPlaces<'a, '_, 'tcx> {
     fn update_bits(
         trans: &mut impl GenKill<MovePathIndex>,
         path: MovePathIndex,
@@ -306,7 +314,7 @@ impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> {
     /// There can be many more `MovePathIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = MaybeReachable<ChunkedBitSet<MovePathIndex>>;
@@ -328,7 +336,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
+impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, '_, 'tcx> {
     type Idx = MovePathIndex;
 
     fn domain_size(&self, _: &Body<'tcx>) -> usize {
@@ -441,7 +449,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> {
     /// There can be many more `MovePathIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<MovePathIndex>;
@@ -465,7 +473,7 @@ impl<'tcx> AnalysisDomain<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
+impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, '_, 'tcx> {
     type Idx = MovePathIndex;
 
     fn domain_size(&self, _: &Body<'tcx>) -> usize {
@@ -642,7 +650,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> {
     /// There can be many more `InitIndex` than there are locals in a MIR body.
     /// We use a chunked bitset to avoid paying too high a memory footprint.
     type Domain = ChunkedBitSet<InitIndex>;
@@ -661,7 +669,7 @@ impl<'tcx> AnalysisDomain<'tcx> for EverInitializedPlaces<'_, 'tcx> {
     }
 }
 
-impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
+impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, '_, 'tcx> {
     type Idx = InitIndex;
 
     fn domain_size(&self, _: &Body<'tcx>) -> usize {
diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
index 29169c31263..f850a710277 100644
--- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs
@@ -288,6 +288,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
             | TerminatorKind::Goto { .. }
             | TerminatorKind::UnwindResume
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::SwitchInt { .. }
             | TerminatorKind::Unreachable => {}
         }
@@ -325,6 +326,7 @@ impl<'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> {
             | TerminatorKind::Goto { .. }
             | TerminatorKind::UnwindResume
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::SwitchInt { .. }
             | TerminatorKind::Unreachable => {}
         }
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 1fb77bef3d4..7b39db821d8 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -489,6 +489,12 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
                     self.gather_init(destination.as_ref(), InitKind::NonPanicPathOnly);
                 }
             }
+            TerminatorKind::TailCall { ref func, ref args, .. } => {
+                self.gather_operand(func);
+                for arg in args {
+                    self.gather_operand(&arg.node);
+                }
+            }
             TerminatorKind::InlineAsm {
                 template: _,
                 ref operands,
diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs
index 5e2d88f94ca..1582c2e8a90 100644
--- a/compiler/rustc_mir_dataflow/src/value_analysis.rs
+++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs
@@ -36,10 +36,10 @@ use std::collections::VecDeque;
 use std::fmt::{Debug, Formatter};
 use std::ops::Range;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, StdEntry};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_index::bit_set::BitSet;
-use rustc_index::{IndexSlice, IndexVec};
+use rustc_index::IndexVec;
 use rustc_middle::bug;
 use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::*;
@@ -269,6 +269,9 @@ pub trait ValueAnalysis<'tcx> {
             TerminatorKind::SwitchInt { discr, targets } => {
                 return self.handle_switch_int(discr, targets, state);
             }
+            TerminatorKind::TailCall { .. } => {
+                // FIXME(explicit_tail_calls): determine if we need to do something here (probably not)
+            }
             TerminatorKind::Goto { .. }
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
@@ -336,14 +339,13 @@ impl<'tcx, T: ValueAnalysis<'tcx>> AnalysisDomain<'tcx> for ValueAnalysisWrapper
     const NAME: &'static str = T::NAME;
 
     fn bottom_value(&self, _body: &Body<'tcx>) -> Self::Domain {
-        State(StateData::Unreachable)
+        State::Unreachable
     }
 
     fn initialize_start_block(&self, body: &Body<'tcx>, state: &mut Self::Domain) {
         // The initial state maps all tracked places of argument projections to ⊤ and the rest to ⊥.
-        assert!(matches!(state.0, StateData::Unreachable));
-        let values = IndexVec::from_elem_n(T::Value::BOTTOM, self.0.map().value_count);
-        *state = State(StateData::Reachable(values));
+        assert!(matches!(state, State::Unreachable));
+        *state = State::new_reachable();
         for arg in body.args_iter() {
             state.flood(PlaceRef { local: arg, projection: &[] }, self.0.map());
         }
@@ -415,27 +417,54 @@ rustc_index::newtype_index!(
 
 /// See [`State`].
 #[derive(PartialEq, Eq, Debug)]
-enum StateData<V> {
-    Reachable(IndexVec<ValueIndex, V>),
-    Unreachable,
+pub struct StateData<V> {
+    bottom: V,
+    /// This map only contains values that are not `⊥`.
+    map: FxHashMap<ValueIndex, V>,
+}
+
+impl<V: HasBottom> StateData<V> {
+    fn new() -> StateData<V> {
+        StateData { bottom: V::BOTTOM, map: FxHashMap::default() }
+    }
+
+    fn get(&self, idx: ValueIndex) -> &V {
+        self.map.get(&idx).unwrap_or(&self.bottom)
+    }
+
+    fn insert(&mut self, idx: ValueIndex, elem: V) {
+        if elem.is_bottom() {
+            self.map.remove(&idx);
+        } else {
+            self.map.insert(idx, elem);
+        }
+    }
 }
 
 impl<V: Clone> Clone for StateData<V> {
     fn clone(&self) -> Self {
-        match self {
-            Self::Reachable(x) => Self::Reachable(x.clone()),
-            Self::Unreachable => Self::Unreachable,
-        }
+        StateData { bottom: self.bottom.clone(), map: self.map.clone() }
     }
 
     fn clone_from(&mut self, source: &Self) {
-        match (&mut *self, source) {
-            (Self::Reachable(x), Self::Reachable(y)) => {
-                // We go through `raw` here, because `IndexVec` currently has a naive `clone_from`.
-                x.raw.clone_from(&y.raw);
+        self.map.clone_from(&source.map)
+    }
+}
+
+impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for StateData<V> {
+    fn join(&mut self, other: &Self) -> bool {
+        let mut changed = false;
+        #[allow(rustc::potential_query_instability)]
+        for (i, v) in other.map.iter() {
+            match self.map.entry(*i) {
+                StdEntry::Vacant(e) => {
+                    e.insert(v.clone());
+                    changed = true
+                }
+                StdEntry::Occupied(e) => changed |= e.into_mut().join(v),
             }
-            _ => *self = source.clone(),
         }
+        changed
     }
 }
 
@@ -450,33 +479,47 @@ impl<V: Clone> Clone for StateData<V> {
 ///
 /// Flooding means assigning a value (by default `⊤`) to all tracked projections of a given place.
 #[derive(PartialEq, Eq, Debug)]
-pub struct State<V>(StateData<V>);
+pub enum State<V> {
+    Unreachable,
+    Reachable(StateData<V>),
+}
 
 impl<V: Clone> Clone for State<V> {
     fn clone(&self) -> Self {
-        Self(self.0.clone())
+        match self {
+            Self::Reachable(x) => Self::Reachable(x.clone()),
+            Self::Unreachable => Self::Unreachable,
+        }
     }
 
     fn clone_from(&mut self, source: &Self) {
-        self.0.clone_from(&source.0);
+        match (&mut *self, source) {
+            (Self::Reachable(x), Self::Reachable(y)) => {
+                x.clone_from(&y);
+            }
+            _ => *self = source.clone(),
+        }
     }
 }
 
-impl<V: Clone> State<V> {
-    pub fn new(init: V, map: &Map) -> State<V> {
-        let values = IndexVec::from_elem_n(init, map.value_count);
-        State(StateData::Reachable(values))
+impl<V: Clone + HasBottom> State<V> {
+    pub fn new_reachable() -> State<V> {
+        State::Reachable(StateData::new())
     }
 
-    pub fn all(&self, f: impl Fn(&V) -> bool) -> bool {
-        match self.0 {
-            StateData::Unreachable => true,
-            StateData::Reachable(ref values) => values.iter().all(f),
+    pub fn all_bottom(&self) -> bool {
+        match self {
+            State::Unreachable => false,
+            State::Reachable(ref values) =>
+            {
+                #[allow(rustc::potential_query_instability)]
+                values.map.values().all(V::is_bottom)
+            }
         }
     }
 
     fn is_reachable(&self) -> bool {
-        matches!(&self.0, StateData::Reachable(_))
+        matches!(self, State::Reachable(_))
     }
 
     /// Assign `value` to all places that are contained in `place` or may alias one.
@@ -519,10 +562,8 @@ impl<V: Clone> State<V> {
         map: &Map,
         value: V,
     ) {
-        let StateData::Reachable(values) = &mut self.0 else { return };
-        map.for_each_aliasing_place(place, tail_elem, &mut |vi| {
-            values[vi] = value.clone();
-        });
+        let State::Reachable(values) = self else { return };
+        map.for_each_aliasing_place(place, tail_elem, &mut |vi| values.insert(vi, value.clone()));
     }
 
     /// Low-level method that assigns to a place.
@@ -541,9 +582,9 @@ impl<V: Clone> State<V> {
     ///
     /// The target place must have been flooded before calling this method.
     pub fn insert_value_idx(&mut self, target: PlaceIndex, value: V, map: &Map) {
-        let StateData::Reachable(values) = &mut self.0 else { return };
+        let State::Reachable(values) = self else { return };
         if let Some(value_index) = map.places[target].value_index {
-            values[value_index] = value;
+            values.insert(value_index, value)
         }
     }
 
@@ -555,14 +596,14 @@ impl<V: Clone> State<V> {
     ///
     /// The target place must have been flooded before calling this method.
     pub fn insert_place_idx(&mut self, target: PlaceIndex, source: PlaceIndex, map: &Map) {
-        let StateData::Reachable(values) = &mut self.0 else { return };
+        let State::Reachable(values) = self else { return };
 
         // If both places are tracked, we copy the value to the target.
         // If the target is tracked, but the source is not, we do nothing, as invalidation has
         // already been performed.
         if let Some(target_value) = map.places[target].value_index {
             if let Some(source_value) = map.places[source].value_index {
-                values[target_value] = values[source_value].clone();
+                values.insert(target_value, values.get(source_value).clone());
             }
         }
         for target_child in map.children(target) {
@@ -616,11 +657,11 @@ impl<V: Clone> State<V> {
 
     /// Retrieve the value stored for a place index, or `None` if it is not tracked.
     pub fn try_get_idx(&self, place: PlaceIndex, map: &Map) -> Option<V> {
-        match &self.0 {
-            StateData::Reachable(values) => {
-                map.places[place].value_index.map(|v| values[v].clone())
+        match self {
+            State::Reachable(values) => {
+                map.places[place].value_index.map(|v| values.get(v).clone())
             }
-            StateData::Unreachable => None,
+            State::Unreachable => None,
         }
     }
 
@@ -631,10 +672,10 @@ impl<V: Clone> State<V> {
     where
         V: HasBottom + HasTop,
     {
-        match &self.0 {
-            StateData::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP),
+        match self {
+            State::Reachable(_) => self.try_get(place, map).unwrap_or(V::TOP),
             // Because this is unreachable, we can return any value we want.
-            StateData::Unreachable => V::BOTTOM,
+            State::Unreachable => V::BOTTOM,
         }
     }
 
@@ -645,10 +686,10 @@ impl<V: Clone> State<V> {
     where
         V: HasBottom + HasTop,
     {
-        match &self.0 {
-            StateData::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP),
+        match self {
+            State::Reachable(_) => self.try_get_discr(place, map).unwrap_or(V::TOP),
             // Because this is unreachable, we can return any value we want.
-            StateData::Unreachable => V::BOTTOM,
+            State::Unreachable => V::BOTTOM,
         }
     }
 
@@ -659,10 +700,10 @@ impl<V: Clone> State<V> {
     where
         V: HasBottom + HasTop,
     {
-        match &self.0 {
-            StateData::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP),
+        match self {
+            State::Reachable(_) => self.try_get_len(place, map).unwrap_or(V::TOP),
             // Because this is unreachable, we can return any value we want.
-            StateData::Unreachable => V::BOTTOM,
+            State::Unreachable => V::BOTTOM,
         }
     }
 
@@ -673,11 +714,11 @@ impl<V: Clone> State<V> {
     where
         V: HasBottom + HasTop,
     {
-        match &self.0 {
-            StateData::Reachable(values) => {
-                map.places[place].value_index.map(|v| values[v].clone()).unwrap_or(V::TOP)
+        match self {
+            State::Reachable(values) => {
+                map.places[place].value_index.map(|v| values.get(v).clone()).unwrap_or(V::TOP)
             }
-            StateData::Unreachable => {
+            State::Unreachable => {
                 // Because this is unreachable, we can return any value we want.
                 V::BOTTOM
             }
@@ -685,15 +726,15 @@ impl<V: Clone> State<V> {
     }
 }
 
-impl<V: JoinSemiLattice + Clone> JoinSemiLattice for State<V> {
+impl<V: JoinSemiLattice + Clone + HasBottom> JoinSemiLattice for State<V> {
     fn join(&mut self, other: &Self) -> bool {
-        match (&mut self.0, &other.0) {
-            (_, StateData::Unreachable) => false,
-            (StateData::Unreachable, _) => {
+        match (&mut *self, other) {
+            (_, State::Unreachable) => false,
+            (State::Unreachable, _) => {
                 *self = other.clone();
                 true
             }
-            (StateData::Reachable(this), StateData::Reachable(other)) => this.join(other),
+            (State::Reachable(this), State::Reachable(ref other)) => this.join(other),
         }
     }
 }
@@ -846,9 +887,10 @@ impl Map {
 
         if let ty::Ref(_, ref_ty, _) | ty::RawPtr(ref_ty, _) = ty.kind()
             && let ty::Slice(..) = ref_ty.kind()
+            // The user may have written a predicate like `[T]: Sized` in their where clauses,
+            // which makes slices scalars.
+            && self.places[place].value_index.is_none()
         {
-            assert!(self.places[place].value_index.is_none(), "slices are not scalars");
-
             // Prepend new child to the linked list.
             let len = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen)));
             self.places[len].next_sibling = self.places[place].first_child;
@@ -1193,9 +1235,9 @@ where
     T::Value: Debug,
 {
     fn fmt_with(&self, ctxt: &ValueAnalysisWrapper<T>, f: &mut Formatter<'_>) -> std::fmt::Result {
-        match &self.0 {
-            StateData::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f),
-            StateData::Unreachable => write!(f, "unreachable"),
+        match self {
+            State::Reachable(values) => debug_with_context(values, None, ctxt.0.map(), f),
+            State::Unreachable => write!(f, "unreachable"),
         }
     }
 
@@ -1205,8 +1247,8 @@ where
         ctxt: &ValueAnalysisWrapper<T>,
         f: &mut Formatter<'_>,
     ) -> std::fmt::Result {
-        match (&self.0, &old.0) {
-            (StateData::Reachable(this), StateData::Reachable(old)) => {
+        match (self, old) {
+            (State::Reachable(this), State::Reachable(old)) => {
                 debug_with_context(this, Some(old), ctxt.0.map(), f)
             }
             _ => Ok(()), // Consider printing something here.
@@ -1214,21 +1256,21 @@ where
     }
 }
 
-fn debug_with_context_rec<V: Debug + Eq>(
+fn debug_with_context_rec<V: Debug + Eq + HasBottom>(
     place: PlaceIndex,
     place_str: &str,
-    new: &IndexSlice<ValueIndex, V>,
-    old: Option<&IndexSlice<ValueIndex, V>>,
+    new: &StateData<V>,
+    old: Option<&StateData<V>>,
     map: &Map,
     f: &mut Formatter<'_>,
 ) -> std::fmt::Result {
     if let Some(value) = map.places[place].value_index {
         match old {
-            None => writeln!(f, "{}: {:?}", place_str, new[value])?,
+            None => writeln!(f, "{}: {:?}", place_str, new.get(value))?,
             Some(old) => {
-                if new[value] != old[value] {
-                    writeln!(f, "\u{001f}-{}: {:?}", place_str, old[value])?;
-                    writeln!(f, "\u{001f}+{}: {:?}", place_str, new[value])?;
+                if new.get(value) != old.get(value) {
+                    writeln!(f, "\u{001f}-{}: {:?}", place_str, old.get(value))?;
+                    writeln!(f, "\u{001f}+{}: {:?}", place_str, new.get(value))?;
                 }
             }
         }
@@ -1260,9 +1302,9 @@ fn debug_with_context_rec<V: Debug + Eq>(
     Ok(())
 }
 
-fn debug_with_context<V: Debug + Eq>(
-    new: &IndexSlice<ValueIndex, V>,
-    old: Option<&IndexSlice<ValueIndex, V>>,
+fn debug_with_context<V: Debug + Eq + HasBottom>(
+    new: &StateData<V>,
+    old: Option<&StateData<V>>,
     map: &Map,
     f: &mut Formatter<'_>,
 ) -> std::fmt::Result {
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index 05674792426..261dcd52d71 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -58,7 +58,7 @@ use crate::deref_separator::deref_finder;
 use crate::errors;
 use crate::pass_manager as pm;
 use crate::simplify;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::pluralize;
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
@@ -81,8 +81,8 @@ use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::abi::{FieldIdx, VariantIdx};
 use rustc_target::spec::PanicStrategy;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt as _;
 use rustc_trait_selection::infer::TyCtxtInferExt as _;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::ObligationCtxt;
 use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode};
 use std::{iter, ops};
@@ -208,11 +208,8 @@ const UNRESUMED: usize = CoroutineArgs::UNRESUMED;
 const RETURNED: usize = CoroutineArgs::RETURNED;
 /// Coroutine has panicked and is poisoned.
 const POISONED: usize = CoroutineArgs::POISONED;
-
-/// Number of variants to reserve in coroutine state. Corresponds to
-/// `UNRESUMED` (beginning of a coroutine) and `RETURNED`/`POISONED`
-/// (end of a coroutine) states.
-const RESERVED_VARIANTS: usize = 3;
+/// Number of reserved variants of coroutine state.
+const RESERVED_VARIANTS: usize = CoroutineArgs::RESERVED_VARIANTS;
 
 /// A `yield` point in the coroutine.
 struct SuspensionPoint<'tcx> {
@@ -236,8 +233,7 @@ struct TransformVisitor<'tcx> {
     discr_ty: Ty<'tcx>,
 
     // Mapping from Local to (type of local, coroutine struct index)
-    // FIXME(eddyb) This should use `IndexVec<Local, Option<_>>`.
-    remap: FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>,
+    remap: IndexVec<Local, Option<(Ty<'tcx>, VariantIdx, FieldIdx)>>,
 
     // A map from a suspension point in a block to the locals which have live storage at that point
     storage_liveness: IndexVec<BasicBlock, Option<BitSet<Local>>>,
@@ -485,7 +481,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
     }
 
     fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
-        assert_eq!(self.remap.get(local), None);
+        assert!(!self.remap.contains(*local));
     }
 
     fn visit_place(
@@ -495,7 +491,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
         _location: Location,
     ) {
         // Replace an Local in the remap with a coroutine struct access
-        if let Some(&(ty, variant_index, idx)) = self.remap.get(&place.local) {
+        if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) {
             replace_base(place, self.make_field(variant_index, idx, ty), self.tcx);
         }
     }
@@ -504,7 +500,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
         // Remove StorageLive and StorageDead statements for remapped locals
         data.retain_statements(|s| match s.kind {
             StatementKind::StorageLive(l) | StatementKind::StorageDead(l) => {
-                !self.remap.contains_key(&l)
+                !self.remap.contains(l)
             }
             _ => true,
         });
@@ -529,13 +525,9 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
 
                 // The resume arg target location might itself be remapped if its base local is
                 // live across a yield.
-                let resume_arg =
-                    if let Some(&(ty, variant, idx)) = self.remap.get(&resume_arg.local) {
-                        replace_base(&mut resume_arg, self.make_field(variant, idx, ty), self.tcx);
-                        resume_arg
-                    } else {
-                        resume_arg
-                    };
+                if let Some(&Some((ty, variant, idx))) = self.remap.get(resume_arg.local) {
+                    replace_base(&mut resume_arg, self.make_field(variant, idx, ty), self.tcx);
+                }
 
                 let storage_liveness: GrowableBitSet<Local> =
                     self.storage_liveness[block].clone().unwrap().into();
@@ -543,7 +535,7 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> {
                 for i in 0..self.always_live_locals.domain_size() {
                     let l = Local::new(i);
                     let needs_storage_dead = storage_liveness.contains(l)
-                        && !self.remap.contains_key(&l)
+                        && !self.remap.contains(l)
                         && !self.always_live_locals.contains(l);
                     if needs_storage_dead {
                         data.statements
@@ -1037,7 +1029,7 @@ fn compute_layout<'tcx>(
     liveness: LivenessInfo,
     body: &Body<'tcx>,
 ) -> (
-    FxHashMap<Local, (Ty<'tcx>, VariantIdx, FieldIdx)>,
+    IndexVec<Local, Option<(Ty<'tcx>, VariantIdx, FieldIdx)>>,
     CoroutineLayout<'tcx>,
     IndexVec<BasicBlock, Option<BitSet<Local>>>,
 ) {
@@ -1098,7 +1090,7 @@ fn compute_layout<'tcx>(
     // Create a map from local indices to coroutine struct indices.
     let mut variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, CoroutineSavedLocal>> =
         iter::repeat(IndexVec::new()).take(RESERVED_VARIANTS).collect();
-    let mut remap = FxHashMap::default();
+    let mut remap = IndexVec::from_elem_n(None, saved_locals.domain_size());
     for (suspension_point_idx, live_locals) in live_locals_at_suspension_points.iter().enumerate() {
         let variant_index = VariantIdx::from(RESERVED_VARIANTS + suspension_point_idx);
         let mut fields = IndexVec::new();
@@ -1109,7 +1101,7 @@ fn compute_layout<'tcx>(
             // around inside coroutines, so it doesn't matter which variant
             // index we access them by.
             let idx = FieldIdx::from_usize(idx);
-            remap.entry(locals[saved_local]).or_insert((tys[saved_local].ty, variant_index, idx));
+            remap[locals[saved_local]] = Some((tys[saved_local].ty, variant_index, idx));
         }
         variant_fields.push(fields);
         variant_source_info.push(source_info_at_suspension_points[suspension_point_idx]);
@@ -1121,7 +1113,9 @@ fn compute_layout<'tcx>(
     for var in &body.var_debug_info {
         let VarDebugInfoContents::Place(place) = &var.value else { continue };
         let Some(local) = place.as_local() else { continue };
-        let Some(&(_, variant, field)) = remap.get(&local) else { continue };
+        let Some(&Some((_, variant, field))) = remap.get(local) else {
+            continue;
+        };
 
         let saved_local = variant_fields[variant][field];
         field_names.get_or_insert_with(saved_local, || var.name);
@@ -1373,6 +1367,10 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool {
             | TerminatorKind::Call { .. }
             | TerminatorKind::InlineAsm { .. }
             | TerminatorKind::Assert { .. } => return true,
+
+            TerminatorKind::TailCall { .. } => {
+                unreachable!("tail calls can't be present in generators")
+            }
         }
     }
 
@@ -1524,7 +1522,7 @@ fn create_cases<'tcx>(
                 for i in 0..(body.local_decls.len()) {
                     let l = Local::new(i);
                     let needs_storage_live = point.storage_liveness.contains(l)
-                        && !transform.remap.contains_key(&l)
+                        && !transform.remap.contains(l)
                         && !transform.always_live_locals.contains(l);
                     if needs_storage_live {
                         statements
@@ -1922,6 +1920,7 @@ impl<'tcx> Visitor<'tcx> for EnsureCoroutineFieldAssignmentsNeverAlias<'_> {
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
             | TerminatorKind::Assert { .. }
diff --git a/compiler/rustc_mir_transform/src/cost_checker.rs b/compiler/rustc_mir_transform/src/cost_checker.rs
index 7e401b5482f..a1c1422912e 100644
--- a/compiler/rustc_mir_transform/src/cost_checker.rs
+++ b/compiler/rustc_mir_transform/src/cost_checker.rs
@@ -31,6 +31,39 @@ impl<'b, 'tcx> CostChecker<'b, 'tcx> {
         CostChecker { tcx, param_env, callee_body, instance, penalty: 0, bonus: 0 }
     }
 
+    /// Add function-level costs not well-represented by the block-level costs.
+    ///
+    /// Needed because the `CostChecker` is used sometimes for just blocks,
+    /// and even the full `Inline` doesn't call `visit_body`, so there's nowhere
+    /// to put this logic in the visitor.
+    pub fn add_function_level_costs(&mut self) {
+        fn is_call_like(bbd: &BasicBlockData<'_>) -> bool {
+            use TerminatorKind::*;
+            match bbd.terminator().kind {
+                Call { .. } | TailCall { .. } | Drop { .. } | Assert { .. } | InlineAsm { .. } => {
+                    true
+                }
+
+                Goto { .. }
+                | SwitchInt { .. }
+                | UnwindResume
+                | UnwindTerminate(_)
+                | Return
+                | Unreachable => false,
+
+                Yield { .. } | CoroutineDrop | FalseEdge { .. } | FalseUnwind { .. } => {
+                    unreachable!()
+                }
+            }
+        }
+
+        // If the only has one Call (or similar), inlining isn't increasing the total
+        // number of calls, so give extra encouragement to inlining that.
+        if self.callee_body.basic_blocks.iter().filter(|bbd| is_call_like(bbd)).count() == 1 {
+            self.bonus += CALL_PENALTY;
+        }
+    }
+
     pub fn cost(&self) -> usize {
         usize::saturating_sub(self.penalty, self.bonus)
     }
@@ -106,6 +139,9 @@ impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
                     self.penalty += LANDINGPAD_PENALTY;
                 }
             }
+            TerminatorKind::TailCall { .. } => {
+                self.penalty += CALL_PENALTY;
+            }
             TerminatorKind::SwitchInt { discr, targets } => {
                 if discr.constant().is_some() {
                     // Not only will this become a `Goto`, but likely other
diff --git a/compiler/rustc_mir_transform/src/coverage/graph.rs b/compiler/rustc_mir_transform/src/coverage/graph.rs
index 360dccb240d..83fb9ff9743 100644
--- a/compiler/rustc_mir_transform/src/coverage/graph.rs
+++ b/compiler/rustc_mir_transform/src/coverage/graph.rs
@@ -358,9 +358,12 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
         }
 
         // These terminators have no coverage-relevant successors.
-        CoroutineDrop | Return | Unreachable | UnwindResume | UnwindTerminate(_) => {
-            CoverageSuccessors::NotChainable(&[])
-        }
+        CoroutineDrop
+        | Return
+        | TailCall { .. }
+        | Unreachable
+        | UnwindResume
+        | UnwindTerminate(_) => CoverageSuccessors::NotChainable(&[]),
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 759bb7c1f9d..25297245172 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -3,15 +3,16 @@ use std::collections::BTreeSet;
 use rustc_data_structures::graph::DirectedGraph;
 use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
-use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, CoverageKind};
+use rustc_middle::mir::coverage::{
+    BlockMarkerId, BranchSpan, ConditionInfo, CoverageInfoHi, CoverageKind,
+};
 use rustc_middle::mir::{self, BasicBlock, StatementKind};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::Span;
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
-use crate::coverage::spans::{
-    extract_refined_covspans, unexpand_into_body_span_with_visible_macro,
-};
+use crate::coverage::spans::extract_refined_covspans;
+use crate::coverage::unexpand::unexpand_into_body_span;
 use crate::coverage::ExtractedHirInfo;
 
 /// Associates an ordinary executable code span with its corresponding BCB.
@@ -158,12 +159,12 @@ impl ExtractedMappings {
 }
 
 fn resolve_block_markers(
-    branch_info: &mir::coverage::BranchInfo,
+    coverage_info_hi: &CoverageInfoHi,
     mir_body: &mir::Body<'_>,
 ) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
     let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
         None,
-        branch_info.num_block_markers,
+        coverage_info_hi.num_block_markers,
     );
 
     // Fill out the mapping from block marker IDs to their enclosing blocks.
@@ -189,11 +190,11 @@ pub(super) fn extract_branch_pairs(
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
 ) -> Vec<BranchPair> {
-    let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return vec![] };
+    let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] };
 
-    let block_markers = resolve_block_markers(branch_info, mir_body);
+    let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
 
-    branch_info
+    coverage_info_hi
         .branch_spans
         .iter()
         .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| {
@@ -202,8 +203,7 @@ pub(super) fn extract_branch_pairs(
             if !raw_span.ctxt().outer_expn_data().is_root() {
                 return None;
             }
-            let (span, _) =
-                unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
+            let span = unexpand_into_body_span(raw_span, hir_info.body_span)?;
 
             let bcb_from_marker =
                 |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
@@ -224,9 +224,9 @@ pub(super) fn extract_mcdc_mappings(
     mcdc_branches: &mut impl Extend<MCDCBranch>,
     mcdc_decisions: &mut impl Extend<MCDCDecision>,
 ) {
-    let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else { return };
+    let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return };
 
-    let block_markers = resolve_block_markers(branch_info, mir_body);
+    let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
 
     let bcb_from_marker =
         |marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
@@ -238,14 +238,14 @@ pub(super) fn extract_mcdc_mappings(
             if !raw_span.ctxt().outer_expn_data().is_root() {
                 return None;
             }
-            let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
+            let span = unexpand_into_body_span(raw_span, body_span)?;
 
             let true_bcb = bcb_from_marker(true_marker)?;
             let false_bcb = bcb_from_marker(false_marker)?;
             Some((span, true_bcb, false_bcb))
         };
 
-    mcdc_branches.extend(branch_info.mcdc_branch_spans.iter().filter_map(
+    mcdc_branches.extend(coverage_info_hi.mcdc_branch_spans.iter().filter_map(
         |&mir::coverage::MCDCBranchSpan {
              span: raw_span,
              condition_info,
@@ -259,9 +259,9 @@ pub(super) fn extract_mcdc_mappings(
         },
     ));
 
-    mcdc_decisions.extend(branch_info.mcdc_decision_spans.iter().filter_map(
+    mcdc_decisions.extend(coverage_info_hi.mcdc_decision_spans.iter().filter_map(
         |decision: &mir::coverage::MCDCDecisionSpan| {
-            let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
+            let span = unexpand_into_body_span(decision.span, body_span)?;
 
             let end_bcbs = decision
                 .end_markers
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 4a64d21f3d1..2efca40d180 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -6,7 +6,12 @@ mod mappings;
 mod spans;
 #[cfg(test)]
 mod tests;
+mod unexpand;
 
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
 use rustc_middle::mir::coverage::{
     CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,
 };
@@ -464,6 +469,9 @@ struct ExtractedHirInfo {
     /// Must have the same context and filename as the body span.
     fn_sig_span_extended: Option<Span>,
     body_span: Span,
+    /// "Holes" are regions within the body span that should not be included in
+    /// coverage spans for this function (e.g. closures and nested items).
+    hole_spans: Vec<Span>,
 }
 
 fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo {
@@ -479,7 +487,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
 
     let mut body_span = hir_body.value.span;
 
-    use rustc_hir::{Closure, Expr, ExprKind, Node};
+    use hir::{Closure, Expr, ExprKind, Node};
     // Unexpand a closure's body span back to the context of its declaration.
     // This helps with closure bodies that consist of just a single bang-macro,
     // and also with closure bodies produced by async desugaring.
@@ -506,11 +514,78 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
 
     let function_source_hash = hash_mir_source(tcx, hir_body);
 
-    ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span_extended, body_span }
+    let hole_spans = extract_hole_spans_from_hir(tcx, body_span, hir_body);
+
+    ExtractedHirInfo {
+        function_source_hash,
+        is_async_fn,
+        fn_sig_span_extended,
+        body_span,
+        hole_spans,
+    }
 }
 
-fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx rustc_hir::Body<'tcx>) -> u64 {
+fn hash_mir_source<'tcx>(tcx: TyCtxt<'tcx>, hir_body: &'tcx hir::Body<'tcx>) -> u64 {
     // FIXME(cjgillot) Stop hashing HIR manually here.
     let owner = hir_body.id().hir_id.owner;
     tcx.hir_owner_nodes(owner).opt_hash_including_bodies.unwrap().to_smaller_hash().as_u64()
 }
+
+fn extract_hole_spans_from_hir<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    body_span: Span, // Usually `hir_body.value.span`, but not always
+    hir_body: &hir::Body<'tcx>,
+) -> Vec<Span> {
+    struct HolesVisitor<'hir, F> {
+        hir: Map<'hir>,
+        visit_hole_span: F,
+    }
+
+    impl<'hir, F: FnMut(Span)> Visitor<'hir> for HolesVisitor<'hir, F> {
+        /// - We need `NestedFilter::INTRA = true` so that `visit_item` will be called.
+        /// - Bodies of nested items don't actually get visited, because of the
+        ///   `visit_item` override.
+        /// - For nested bodies that are not part of an item, we do want to visit any
+        ///   items contained within them.
+        type NestedFilter = nested_filter::All;
+
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.hir
+        }
+
+        fn visit_item(&mut self, item: &'hir hir::Item<'hir>) {
+            (self.visit_hole_span)(item.span);
+            // Having visited this item, we don't care about its children,
+            // so don't call `walk_item`.
+        }
+
+        // We override `visit_expr` instead of the more specific expression
+        // visitors, so that we have direct access to the expression span.
+        fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
+            match expr.kind {
+                hir::ExprKind::Closure(_) | hir::ExprKind::ConstBlock(_) => {
+                    (self.visit_hole_span)(expr.span);
+                    // Having visited this expression, we don't care about its
+                    // children, so don't call `walk_expr`.
+                }
+
+                // For other expressions, recursively visit as normal.
+                _ => walk_expr(self, expr),
+            }
+        }
+    }
+
+    let mut hole_spans = vec![];
+    let mut visitor = HolesVisitor {
+        hir: tcx.hir(),
+        visit_hole_span: |hole_span| {
+            // Discard any holes that aren't directly visible within the body span.
+            if body_span.contains(hole_span) && body_span.eq_ctxt(hole_span) {
+                hole_spans.push(hole_span);
+            }
+        },
+    };
+
+    visitor.visit_body(hir_body);
+    hole_spans
+}
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 84a70d1f02d..dbc26a2808e 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -8,25 +8,20 @@ use rustc_span::Span;
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
 use crate::coverage::mappings;
 use crate::coverage::spans::from_mir::{
-    extract_covspans_and_holes_from_mir, ExtractedCovspans, Hole, SpanFromMir,
+    extract_covspans_from_mir, ExtractedCovspans, Hole, SpanFromMir,
 };
 use crate::coverage::ExtractedHirInfo;
 
 mod from_mir;
 
-// FIXME(#124545) It's awkward that we have to re-export this, because it's an
-// internal detail of `from_mir` that is also needed when handling branch and
-// MC/DC spans. Ideally we would find a more natural home for it.
-pub(super) use from_mir::unexpand_into_body_span_with_visible_macro;
-
 pub(super) fn extract_refined_covspans(
     mir_body: &mir::Body<'_>,
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
     code_mappings: &mut impl Extend<mappings::CodeMapping>,
 ) {
-    let ExtractedCovspans { mut covspans, mut holes } =
-        extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks);
+    let ExtractedCovspans { mut covspans } =
+        extract_covspans_from_mir(mir_body, hir_info, basic_coverage_blocks);
 
     // First, perform the passes that need macro information.
     covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
@@ -50,6 +45,7 @@ pub(super) fn extract_refined_covspans(
     covspans.dedup_by(|b, a| a.span.source_equal(b.span));
 
     // Sort the holes, and merge overlapping/adjacent holes.
+    let mut holes = hir_info.hole_spans.iter().map(|&span| Hole { span }).collect::<Vec<_>>();
     holes.sort_by(|a, b| compare_spans(a.span, b.span));
     holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
 
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 09deb7534bf..32bd25bf4b9 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -1,26 +1,25 @@
 use rustc_middle::bug;
 use rustc_middle::mir::coverage::CoverageKind;
 use rustc_middle::mir::{
-    self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
-    TerminatorKind,
+    self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
 };
-use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
+use rustc_span::{Span, Symbol};
 
 use crate::coverage::graph::{
     BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
 };
 use crate::coverage::spans::Covspan;
+use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
 use crate::coverage::ExtractedHirInfo;
 
 pub(crate) struct ExtractedCovspans {
     pub(crate) covspans: Vec<SpanFromMir>,
-    pub(crate) holes: Vec<Hole>,
 }
 
 /// Traverses the MIR body to produce an initial collection of coverage-relevant
 /// spans, each associated with a node in the coverage graph (BCB) and possibly
 /// other metadata.
-pub(crate) fn extract_covspans_and_holes_from_mir(
+pub(crate) fn extract_covspans_from_mir(
     mir_body: &mir::Body<'_>,
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
@@ -28,21 +27,13 @@ pub(crate) fn extract_covspans_and_holes_from_mir(
     let &ExtractedHirInfo { body_span, .. } = hir_info;
 
     let mut covspans = vec![];
-    let mut holes = vec![];
 
     for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
-        bcb_to_initial_coverage_spans(
-            mir_body,
-            body_span,
-            bcb,
-            bcb_data,
-            &mut covspans,
-            &mut holes,
-        );
+        bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans);
     }
 
     // Only add the signature span if we found at least one span in the body.
-    if !covspans.is_empty() || !holes.is_empty() {
+    if !covspans.is_empty() {
         // If there is no usable signature span, add a fake one (before refinement)
         // to avoid an ugly gap between the body start and the first real span.
         // FIXME: Find a more principled way to solve this problem.
@@ -50,7 +41,7 @@ pub(crate) fn extract_covspans_and_holes_from_mir(
         covspans.push(SpanFromMir::for_fn_sig(fn_sig_span));
     }
 
-    ExtractedCovspans { covspans, holes }
+    ExtractedCovspans { covspans }
 }
 
 // Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
@@ -64,7 +55,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
     bcb: BasicCoverageBlock,
     bcb_data: &'a BasicCoverageBlockData,
     initial_covspans: &mut Vec<SpanFromMir>,
-    holes: &mut Vec<Hole>,
 ) {
     for &bb in &bcb_data.basic_blocks {
         let data = &mir_body[bb];
@@ -80,13 +70,7 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
             let expn_span = filtered_statement_span(statement)?;
             let (span, visible_macro) = unexpand(expn_span)?;
 
-            // A statement that looks like the assignment of a closure expression
-            // is treated as a "hole" span, to be carved out of other spans.
-            if is_closure_like(statement) {
-                holes.push(Hole { span });
-            } else {
-                initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
-            }
+            initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
             Some(())
         };
         for statement in data.statements.iter() {
@@ -104,18 +88,6 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
     }
 }
 
-fn is_closure_like(statement: &Statement<'_>) -> bool {
-    match statement.kind {
-        StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind {
-            AggregateKind::Closure(_, _)
-            | AggregateKind::Coroutine(_, _)
-            | AggregateKind::CoroutineClosure(..) => true,
-            _ => false,
-        },
-        _ => false,
-    }
-}
-
 /// If the MIR `Statement` has a span contributive to computing coverage spans,
 /// return it; otherwise return `None`.
 fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
@@ -192,7 +164,8 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
         | TerminatorKind::Goto { .. } => None,
 
         // Call `func` operand can have a more specific span when part of a chain of calls
-        | TerminatorKind::Call { ref func, .. } => {
+        TerminatorKind::Call { ref func, .. }
+        | TerminatorKind::TailCall { ref func, .. } => {
             let mut span = terminator.source_info.span;
             if let mir::Operand::Constant(box constant) = func {
                 if constant.span.lo() > span.lo() {
@@ -215,59 +188,6 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
     }
 }
 
-/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
-/// within the function's body source. This span is guaranteed to be contained
-/// within, or equal to, the `body_span`. If the extrapolated span is not
-/// contained within the `body_span`, `None` is returned.
-///
-/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
-/// etc.).
-pub(crate) 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)]
 pub(crate) struct Hole {
     pub(crate) span: Span,
diff --git a/compiler/rustc_mir_transform/src/coverage/unexpand.rs b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
new file mode 100644
index 00000000000..8cde291b907
--- /dev/null
+++ b/compiler/rustc_mir_transform/src/coverage/unexpand.rs
@@ -0,0 +1,60 @@
+use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
+
+/// Walks through the expansion ancestors of `original_span` to find a span that
+/// is contained in `body_span` and has the same [syntax context] as `body_span`.
+pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> Option<Span> {
+    // Because we don't need to return any extra ancestor information,
+    // we can just delegate directly to `find_ancestor_inside_same_ctxt`.
+    original_span.find_ancestor_inside_same_ctxt(body_span)
+}
+
+/// Walks through the expansion ancestors of `original_span` to find a span that
+/// is contained in `body_span` and has the same [syntax context] as `body_span`.
+///
+/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
+/// the returned symbol will be the name of that macro (e.g. `foo`).
+pub(crate) 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 [syntax context] 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)`
+/// - `prev.parent_callsite() == ancestor`
+///
+/// [syntax context]: 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_inside_same_ctxt(body_span));
+    if let Some(prev) = prev {
+        debug_assert_eq!(Some(curr), prev.parent_callsite());
+    }
+
+    Some((curr, prev))
+}
diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
index 0fd85eb345d..8b965f4d18e 100644
--- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
+++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs
@@ -181,11 +181,6 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
                         state.insert_value_idx(value_target, val, self.map());
                     }
                     if let Some(overflow_target) = overflow_target {
-                        let overflow = match overflow {
-                            FlatSet::Top => FlatSet::Top,
-                            FlatSet::Elem(overflow) => FlatSet::Elem(overflow),
-                            FlatSet::Bottom => FlatSet::Bottom,
-                        };
                         // We have flooded `target` earlier.
                         state.insert_value_idx(overflow_target, overflow, self.map());
                     }
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index b1016c0867c..ab73a8af317 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -628,6 +628,12 @@ impl WriteInfo {
                     self.add_operand(&arg.node);
                 }
             }
+            TerminatorKind::TailCall { func, args, .. } => {
+                self.add_operand(func);
+                for arg in args {
+                    self.add_operand(&arg.node);
+                }
+            }
             TerminatorKind::InlineAsm { operands, .. } => {
                 for asm_operand in operands {
                     match asm_operand {
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 665b2260294..25bebb0539a 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -33,16 +33,16 @@ use std::fmt;
 /// as it would allow running a destructor on a place behind a reference:
 ///
 /// ```text
-// fn drop_term<T>(t: &mut T) {
-//     mir! {
-//         {
-//             Drop(*t, exit)
-//         }
-//         exit = {
-//             Return()
-//         }
-//     }
-// }
+/// fn drop_term<T>(t: &mut T) {
+///     mir! {
+///         {
+///             Drop(*t, exit)
+///         }
+///         exit = {
+///             Return()
+///         }
+///     }
+/// }
 /// ```
 pub struct ElaborateDrops;
 
@@ -97,7 +97,7 @@ impl<'tcx> MirPass<'tcx> for ElaborateDrops {
 #[instrument(level = "trace", skip(body, flow_inits), ret)]
 fn compute_dead_unwinds<'mir, 'tcx>(
     body: &'mir Body<'tcx>,
-    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
+    flow_inits: &mut ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'_, 'mir, 'tcx>>,
 ) -> BitSet<BasicBlock> {
     // We only need to do this pass once, because unwind edges can only
     // reach cleanup blocks, which can't have unwind edges themselves.
@@ -118,12 +118,12 @@ fn compute_dead_unwinds<'mir, 'tcx>(
     dead_unwinds
 }
 
-struct InitializationData<'mir, 'tcx> {
-    inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'mir, 'tcx>>,
-    uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'mir, 'tcx>>,
+struct InitializationData<'a, 'mir, 'tcx> {
+    inits: ResultsCursor<'mir, 'tcx, MaybeInitializedPlaces<'a, 'mir, 'tcx>>,
+    uninits: ResultsCursor<'mir, 'tcx, MaybeUninitializedPlaces<'a, 'mir, 'tcx>>,
 }
 
-impl InitializationData<'_, '_> {
+impl InitializationData<'_, '_, '_> {
     fn seek_before(&mut self, loc: Location) {
         self.inits.seek_before_primary_effect(loc);
         self.uninits.seek_before_primary_effect(loc);
@@ -134,17 +134,17 @@ impl InitializationData<'_, '_> {
     }
 }
 
-struct Elaborator<'a, 'b, 'tcx> {
-    ctxt: &'a mut ElaborateDropsCtxt<'b, 'tcx>,
+struct Elaborator<'a, 'b, 'mir, 'tcx> {
+    ctxt: &'a mut ElaborateDropsCtxt<'b, 'mir, 'tcx>,
 }
 
-impl fmt::Debug for Elaborator<'_, '_, '_> {
+impl fmt::Debug for Elaborator<'_, '_, '_, '_> {
     fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
         Ok(())
     }
 }
 
-impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> {
+impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, '_, 'tcx> {
     type Path = MovePathIndex;
 
     fn patch(&mut self) -> &mut MirPatch<'tcx> {
@@ -238,16 +238,16 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for Elaborator<'a, '_, 'tcx> {
     }
 }
 
-struct ElaborateDropsCtxt<'a, 'tcx> {
+struct ElaborateDropsCtxt<'a, 'mir, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    body: &'a Body<'tcx>,
+    body: &'mir Body<'tcx>,
     env: &'a MoveDataParamEnv<'tcx>,
-    init_data: InitializationData<'a, 'tcx>,
+    init_data: InitializationData<'a, 'mir, 'tcx>,
     drop_flags: IndexVec<MovePathIndex, Option<Local>>,
     patch: MirPatch<'tcx>,
 }
 
-impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
+impl<'b, 'mir, 'tcx> ElaborateDropsCtxt<'b, 'mir, 'tcx> {
     fn move_data(&self) -> &'b MoveData<'tcx> {
         &self.env.move_data
     }
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 936a7e2d9de..1002746e553 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -8,7 +8,7 @@
 //! `Value` is interned as a `VnIndex`, which allows us to cheaply compute identical values.
 //!
 //! From those assignments, we construct a mapping `VnIndex -> Vec<(Local, Location)>` of available
-//! values, the locals in which they are stored, and a the assignment location.
+//! values, the locals in which they are stored, and the assignment location.
 //!
 //! In a second pass, we traverse all (non SSA) assignments `x = rvalue` and operands. For each
 //! one, we compute the `VnIndex` of the rvalue. If this `VnIndex` is associated to a constant, we
@@ -1074,11 +1074,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
         {
             lhs = *lhs_value;
             rhs = *rhs_value;
-            if let Some(op) = self.try_as_operand(lhs, location) {
-                *lhs_operand = op;
-            }
-            if let Some(op) = self.try_as_operand(rhs, location) {
-                *rhs_operand = op;
+            if let Some(lhs_op) = self.try_as_operand(lhs, location)
+                && let Some(rhs_op) = self.try_as_operand(rhs, location)
+            {
+                *lhs_operand = lhs_op;
+                *rhs_operand = rhs_op;
             }
         }
 
@@ -1391,11 +1391,15 @@ fn op_to_prop_const<'tcx>(
         let (prov, offset) = pointer.into_parts();
         let alloc_id = prov.alloc_id();
         intern_const_alloc_for_constprop(ecx, alloc_id).ok()?;
-        if matches!(ecx.tcx.global_alloc(alloc_id), GlobalAlloc::Memory(_)) {
-            // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything
-            // by `GlobalAlloc::Memory`, so do fall through to copying if needed.
-            // FIXME: find a way to treat this more uniformly
-            // (probably by fixing codegen)
+
+        // `alloc_id` may point to a static. Codegen will choke on an `Indirect` with anything
+        // by `GlobalAlloc::Memory`, so do fall through to copying if needed.
+        // FIXME: find a way to treat this more uniformly (probably by fixing codegen)
+        if let GlobalAlloc::Memory(alloc) = ecx.tcx.global_alloc(alloc_id)
+            // Transmuting a constant is just an offset in the allocation. If the alignment of the
+            // allocation is not enough, fallback to copying into a properly aligned value.
+            && alloc.inner().align >= op.layout.align.abi
+        {
             return Some(ConstValue::Indirect { alloc_id, offset });
         }
     }
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 0a5fc697d03..fd9f0fec88d 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -11,7 +11,7 @@ use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}
 use rustc_middle::mir::visit::*;
 use rustc_middle::mir::*;
 use rustc_middle::ty::TypeVisitableExt;
-use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt};
+use rustc_middle::ty::{self, Instance, InstanceKind, ParamEnv, Ty, TyCtxt, TypeFlags};
 use rustc_session::config::{DebugInfo, OptLevel};
 use rustc_span::source_map::Spanned;
 use rustc_span::sym;
@@ -41,6 +41,12 @@ struct CallSite<'tcx> {
 
 impl<'tcx> MirPass<'tcx> for Inline {
     fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
+        // FIXME(#127234): Coverage instrumentation currently doesn't handle inlined
+        // MIR correctly when Modified Condition/Decision Coverage is enabled.
+        if sess.instrument_coverage_mcdc() {
+            return false;
+        }
+
         if let Some(enabled) = sess.opts.unstable_opts.inline_mir {
             return enabled;
         }
@@ -85,13 +91,18 @@ fn inline<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> bool {
     }
 
     let param_env = tcx.param_env_reveal_all_normalized(def_id);
+    let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
 
     let mut this = Inliner {
         tcx,
         param_env,
-        codegen_fn_attrs: tcx.codegen_fn_attrs(def_id),
+        codegen_fn_attrs,
         history: Vec::new(),
         changed: false,
+        caller_is_inline_forwarder: matches!(
+            codegen_fn_attrs.inline,
+            InlineAttr::Hint | InlineAttr::Always
+        ) && body_is_forwarder(body),
     };
     let blocks = START_BLOCK..body.basic_blocks.next_index();
     this.process_blocks(body, blocks);
@@ -111,6 +122,9 @@ struct Inliner<'tcx> {
     history: Vec<DefId>,
     /// Indicates that the caller body has been modified.
     changed: bool,
+    /// Indicates that the caller is #[inline] and just calls another function,
+    /// and thus we can inline less into it as it'll be inlined itself.
+    caller_is_inline_forwarder: bool,
 }
 
 impl<'tcx> Inliner<'tcx> {
@@ -306,6 +320,16 @@ impl<'tcx> Inliner<'tcx> {
             InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => {
                 return Err("instance without MIR (intrinsic / virtual)");
             }
+
+            // FIXME(#127030): `ConstParamHasTy` has bad interactions with
+            // the drop shim builder, which does not evaluate predicates in
+            // the correct param-env for types being dropped. Stall resolving
+            // the MIR for this instance until all of its const params are
+            // substituted.
+            InstanceKind::DropGlue(_, Some(ty)) if ty.has_type_flags(TypeFlags::HAS_CT_PARAM) => {
+                return Err("still needs substitution");
+            }
+
             // This cannot result in an immediate cycle since the callee MIR is a shim, which does
             // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we
             // do not need to catch this here, we can wait until the inliner decides to continue
@@ -365,13 +389,15 @@ impl<'tcx> Inliner<'tcx> {
     ) -> Option<CallSite<'tcx>> {
         // Only consider direct calls to functions
         let terminator = bb_data.terminator();
+
+        // FIXME(explicit_tail_calls): figure out if we can inline tail calls
         if let TerminatorKind::Call { ref func, fn_span, .. } = terminator.kind {
             let func_ty = func.ty(caller_body, self.tcx);
             if let ty::FnDef(def_id, args) = *func_ty.kind() {
                 // To resolve an instance its args have to be fully normalized.
                 let args = self.tcx.try_normalize_erasing_regions(self.param_env, args).ok()?;
                 let callee =
-                    Instance::resolve(self.tcx, self.param_env, def_id, args).ok().flatten()?;
+                    Instance::try_resolve(self.tcx, self.param_env, def_id, args).ok().flatten()?;
 
                 if let InstanceKind::Virtual(..) | InstanceKind::Intrinsic(_) = callee.def {
                     return None;
@@ -475,7 +501,9 @@ impl<'tcx> Inliner<'tcx> {
     ) -> Result<(), &'static str> {
         let tcx = self.tcx;
 
-        let mut threshold = if cross_crate_inlinable {
+        let mut threshold = if self.caller_is_inline_forwarder {
+            self.tcx.sess.opts.unstable_opts.inline_mir_forwarder_threshold.unwrap_or(30)
+        } else if cross_crate_inlinable {
             self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100)
         } else {
             self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50)
@@ -494,6 +522,8 @@ impl<'tcx> Inliner<'tcx> {
         let mut checker =
             CostChecker::new(self.tcx, self.param_env, Some(callsite.callee), callee_body);
 
+        checker.add_function_level_costs();
+
         // Traverse the MIR manually so we can account for the effects of inlining on the CFG.
         let mut work_list = vec![START_BLOCK];
         let mut visited = BitSet::new_empty(callee_body.basic_blocks.len());
@@ -528,6 +558,9 @@ impl<'tcx> Inliner<'tcx> {
                 // inline-asm is detected. LLVM will still possibly do an inline later on
                 // if the no-attribute function ends up with the same instruction set anyway.
                 return Err("Cannot move inline-asm across instruction sets");
+            } else if let TerminatorKind::TailCall { .. } = term.kind {
+                // FIXME(explicit_tail_calls): figure out how exactly functions containing tail calls can be inlined (and if they even should)
+                return Err("can't inline functions with tail calls");
             } else {
                 work_list.extend(term.successors())
             }
@@ -1016,6 +1049,10 @@ impl<'tcx> MutVisitor<'tcx> for Integrator<'_, 'tcx> {
                 *target = self.map_block(*target);
                 *unwind = self.map_unwind(*unwind);
             }
+            TerminatorKind::TailCall { .. } => {
+                // check_mir_body forbids tail calls
+                unreachable!()
+            }
             TerminatorKind::Call { ref mut target, ref mut unwind, .. } => {
                 if let Some(ref mut tgt) = *target {
                     *tgt = self.map_block(*tgt);
@@ -1081,3 +1118,37 @@ fn try_instance_mir<'tcx>(
     }
     Ok(tcx.instance_mir(instance))
 }
+
+fn body_is_forwarder(body: &Body<'_>) -> bool {
+    let TerminatorKind::Call { target, .. } = body.basic_blocks[START_BLOCK].terminator().kind
+    else {
+        return false;
+    };
+    if let Some(target) = target {
+        let TerminatorKind::Return = body.basic_blocks[target].terminator().kind else {
+            return false;
+        };
+    }
+
+    let max_blocks = if !body.is_polymorphic {
+        2
+    } else if target.is_none() {
+        3
+    } else {
+        4
+    };
+    if body.basic_blocks.len() > max_blocks {
+        return false;
+    }
+
+    body.basic_blocks.iter_enumerated().all(|(bb, bb_data)| {
+        bb == START_BLOCK
+            || matches!(
+                bb_data.terminator().kind,
+                TerminatorKind::Return
+                    | TerminatorKind::Drop { .. }
+                    | TerminatorKind::UnwindResume
+                    | TerminatorKind::UnwindTerminate(_)
+            )
+    })
+}
diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs
index 35bcd24ce95..d4477563e3a 100644
--- a/compiler/rustc_mir_transform/src/inline/cycle.rs
+++ b/compiler/rustc_mir_transform/src/inline/cycle.rs
@@ -53,7 +53,7 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
                 trace!(?caller, ?param_env, ?args, "cannot normalize, skipping");
                 continue;
             };
-            let Ok(Some(callee)) = ty::Instance::resolve(tcx, param_env, callee, args) else {
+            let Ok(Some(callee)) = ty::Instance::try_resolve(tcx, param_env, callee, args) else {
                 trace!(?callee, "cannot resolve, skipping");
                 continue;
             };
diff --git a/compiler/rustc_mir_transform/src/jump_threading.rs b/compiler/rustc_mir_transform/src/jump_threading.rs
index 23cc0c46e73..97ec0cb39de 100644
--- a/compiler/rustc_mir_transform/src/jump_threading.rs
+++ b/compiler/rustc_mir_transform/src/jump_threading.rs
@@ -47,6 +47,7 @@ use rustc_middle::mir::visit::Visitor;
 use rustc_middle::mir::*;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::{self, ScalarInt, TyCtxt};
+use rustc_mir_dataflow::lattice::HasBottom;
 use rustc_mir_dataflow::value_analysis::{Map, PlaceIndex, State, TrackElem};
 use rustc_span::DUMMY_SP;
 use rustc_target::abi::{TagEncoding, Variants};
@@ -91,43 +92,8 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
             opportunities: Vec::new(),
         };
 
-        for (bb, bbdata) in body.basic_blocks.iter_enumerated() {
-            debug!(?bb, term = ?bbdata.terminator());
-            if bbdata.is_cleanup || loop_headers.contains(bb) {
-                continue;
-            }
-            let Some((discr, targets)) = bbdata.terminator().kind.as_switch() else { continue };
-            let Some(discr) = discr.place() else { continue };
-            debug!(?discr, ?bb);
-
-            let discr_ty = discr.ty(body, tcx).ty;
-            let Ok(discr_layout) = finder.ecx.layout_of(discr_ty) else { continue };
-
-            let Some(discr) = finder.map.find(discr.as_ref()) else { continue };
-            debug!(?discr);
-
-            let cost = CostChecker::new(tcx, param_env, None, body);
-
-            let mut state = State::new(ConditionSet::default(), finder.map);
-
-            let conds = if let Some((value, then, else_)) = targets.as_static_if() {
-                let Some(value) = ScalarInt::try_from_uint(value, discr_layout.size) else {
-                    continue;
-                };
-                arena.alloc_from_iter([
-                    Condition { value, polarity: Polarity::Eq, target: then },
-                    Condition { value, polarity: Polarity::Ne, target: else_ },
-                ])
-            } else {
-                arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| {
-                    let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
-                    Some(Condition { value, polarity: Polarity::Eq, target })
-                }))
-            };
-            let conds = ConditionSet(conds);
-            state.insert_value_idx(discr, conds, finder.map);
-
-            finder.find_opportunity(bb, state, cost, 0);
+        for bb in body.basic_blocks.indices() {
+            finder.start_from_switch(bb);
         }
 
         let opportunities = finder.opportunities;
@@ -193,9 +159,17 @@ impl Condition {
     }
 }
 
-#[derive(Copy, Clone, Debug, Default)]
+#[derive(Copy, Clone, Debug)]
 struct ConditionSet<'a>(&'a [Condition]);
 
+impl HasBottom for ConditionSet<'_> {
+    const BOTTOM: Self = ConditionSet(&[]);
+
+    fn is_bottom(&self) -> bool {
+        self.0.is_empty()
+    }
+}
+
 impl<'a> ConditionSet<'a> {
     fn iter(self) -> impl Iterator<Item = Condition> + 'a {
         self.0.iter().copied()
@@ -212,10 +186,50 @@ impl<'a> ConditionSet<'a> {
 
 impl<'tcx, 'a> TOFinder<'tcx, 'a> {
     fn is_empty(&self, state: &State<ConditionSet<'a>>) -> bool {
-        state.all(|cs| cs.0.is_empty())
+        state.all_bottom()
     }
 
     /// Recursion entry point to find threading opportunities.
+    #[instrument(level = "trace", skip(self))]
+    fn start_from_switch(&mut self, bb: BasicBlock) -> Option<!> {
+        let bbdata = &self.body[bb];
+        if bbdata.is_cleanup || self.loop_headers.contains(bb) {
+            return None;
+        }
+        let (discr, targets) = bbdata.terminator().kind.as_switch()?;
+        let discr = discr.place()?;
+        debug!(?discr, ?bb);
+
+        let discr_ty = discr.ty(self.body, self.tcx).ty;
+        let discr_layout = self.ecx.layout_of(discr_ty).ok()?;
+
+        let discr = self.map.find(discr.as_ref())?;
+        debug!(?discr);
+
+        let cost = CostChecker::new(self.tcx, self.param_env, None, self.body);
+        let mut state = State::new_reachable();
+
+        let conds = if let Some((value, then, else_)) = targets.as_static_if() {
+            let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+            self.arena.alloc_from_iter([
+                Condition { value, polarity: Polarity::Eq, target: then },
+                Condition { value, polarity: Polarity::Ne, target: else_ },
+            ])
+        } else {
+            self.arena.alloc_from_iter(targets.iter().filter_map(|(value, target)| {
+                let value = ScalarInt::try_from_uint(value, discr_layout.size)?;
+                Some(Condition { value, polarity: Polarity::Eq, target })
+            }))
+        };
+        let conds = ConditionSet(conds);
+        state.insert_value_idx(discr, conds, self.map);
+
+        self.find_opportunity(bb, state, cost, 0);
+        None
+    }
+
+    /// Recursively walk statements backwards from this bb's terminator to find threading
+    /// opportunities.
     #[instrument(level = "trace", skip(self, cost), ret)]
     fn find_opportunity(
         &mut self,
@@ -250,7 +264,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             //   _1 = 5 // Whatever happens here, it won't change the result of a `SwitchInt`.
             //   _1 = 6
             if let Some((lhs, tail)) = self.mutated_statement(stmt) {
-                state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::default());
+                state.flood_with_tail_elem(lhs.as_ref(), tail, self.map, ConditionSet::BOTTOM);
             }
         }
 
@@ -270,12 +284,13 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
                     self.process_switch_int(discr, targets, bb, &mut state);
                     self.find_opportunity(pred, state, cost, depth + 1);
                 }
-                _ => self.recurse_through_terminator(pred, &state, &cost, depth),
+                _ => self.recurse_through_terminator(pred, || state, &cost, depth),
             }
-        } else {
+        } else if let &[ref predecessors @ .., last_pred] = &predecessors[..] {
             for &pred in predecessors {
-                self.recurse_through_terminator(pred, &state, &cost, depth);
+                self.recurse_through_terminator(pred, || state.clone(), &cost, depth);
             }
+            self.recurse_through_terminator(last_pred, || state, &cost, depth);
         }
 
         let new_tos = &mut self.opportunities[last_non_rec..];
@@ -566,11 +581,12 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         None
     }
 
-    #[instrument(level = "trace", skip(self, cost))]
+    #[instrument(level = "trace", skip(self, state, cost))]
     fn recurse_through_terminator(
         &mut self,
         bb: BasicBlock,
-        state: &State<ConditionSet<'a>>,
+        // Pass a closure that may clone the state, as we don't want to do it each time.
+        state: impl FnOnce() -> State<ConditionSet<'a>>,
         cost: &CostChecker<'_, 'tcx>,
         depth: usize,
     ) {
@@ -580,6 +596,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
             TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::CoroutineDrop => bug!("{term:?} has no terminators"),
             // Disallowed during optimizations.
@@ -600,9 +617,9 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
         };
 
         // We can recurse through this terminator.
-        let mut state = state.clone();
+        let mut state = state();
         if let Some(place_to_flood) = place_to_flood {
-            state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::default());
+            state.flood_with(place_to_flood.as_ref(), self.map, ConditionSet::BOTTOM);
         }
         self.find_opportunity(bb, state, cost.clone(), depth + 1);
     }
diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 8d6c00bbedb..82ad8879d17 100644
--- a/compiler/rustc_mir_transform/src/known_panics_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -799,6 +799,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
             | TerminatorKind::UnwindResume
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Return
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Unreachable
             | TerminatorKind::Drop { .. }
             | TerminatorKind::Yield { .. }
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index f7056702cb4..5d253d7384d 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -519,7 +519,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &add_subtyping_projections::Subtyper, // calling this after reveal_all ensures that we don't deal with opaque types
         &elaborate_drops::ElaborateDrops,
         // This will remove extraneous landing pads which are no longer
-        // necessary as well as well as forcing any call in a non-unwinding
+        // necessary as well as forcing any call in a non-unwinding
         // function calling a possibly-unwinding function to abort the process.
         &abort_unwinding_calls::AbortUnwindingCalls,
         // AddMovesForPackedDrops needs to run after drop
diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs
index db2bb60bdac..d928d7cf764 100644
--- a/compiler/rustc_mir_transform/src/mentioned_items.rs
+++ b/compiler/rustc_mir_transform/src/mentioned_items.rs
@@ -38,7 +38,7 @@ impl<'tcx> Visitor<'tcx> for MentionedItemsVisitor<'_, 'tcx> {
         self.super_terminator(terminator, location);
         let span = || self.body.source_info(location).span;
         match &terminator.kind {
-            mir::TerminatorKind::Call { func, .. } => {
+            mir::TerminatorKind::Call { func, .. } | mir::TerminatorKind::TailCall { func, .. } => {
                 let callee_ty = func.ty(self.body, self.tcx);
                 self.mentioned_items
                     .push(Spanned { node: MentionedItem::Fn(callee_ty), span: span() });
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index 3f4d2b65ff2..736647fb64b 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -816,7 +816,7 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
                     mut func, mut args, call_source: desugar, fn_span, ..
                 } => {
                     // This promoted involves a function call, so it may fail to evaluate.
-                    // Let's make sure it is added to `required_consts` so that that failure cannot get lost.
+                    // Let's make sure it is added to `required_consts` so that failure cannot get lost.
                     self.add_to_required = true;
 
                     self.visit_operand(&mut func, loc);
diff --git a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
index fb52bfa468a..1df5737e859 100644
--- a/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
+++ b/compiler/rustc_mir_transform/src/remove_noop_landing_pads.rs
@@ -75,6 +75,7 @@ impl RemoveNoopLandingPads {
             | TerminatorKind::UnwindTerminate(_)
             | TerminatorKind::Unreachable
             | TerminatorKind::Call { .. }
+            | TerminatorKind::TailCall { .. }
             | TerminatorKind::Assert { .. }
             | TerminatorKind::Drop { .. }
             | TerminatorKind::InlineAsm { .. } => false,
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index 25577e88e28..6835a39cf36 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -1,18 +1,17 @@
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::lang_items::LangItem;
+use rustc_index::{Idx, IndexVec};
 use rustc_middle::mir::*;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
-use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
-
-use rustc_index::{Idx, IndexVec};
-
 use rustc_span::{source_map::Spanned, Span, DUMMY_SP};
+use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
 use rustc_target::spec::abi::Abi;
 
+use std::assert_matches::assert_matches;
 use std::fmt;
 use std::iter;
 
@@ -1020,21 +1019,19 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
     receiver_by_ref: bool,
 ) -> Body<'tcx> {
     let mut self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity();
+    let mut self_local: Place<'tcx> = Local::from_usize(1).into();
     let ty::CoroutineClosure(_, args) = *self_ty.kind() else {
         bug!();
     };
 
-    // We use `&mut Self` here because we only need to emit an ABI-compatible shim body,
-    // rather than match the signature exactly (which might take `&self` instead).
+    // We use `&Self` here because we only need to emit an ABI-compatible shim body,
+    // rather than match the signature exactly (which might take `&mut self` instead).
     //
-    // The self type here is a coroutine-closure, not a coroutine, and we never read from
-    // it because it never has any captures, because this is only true in the Fn/FnMut
-    // implementation, not the AsyncFn/AsyncFnMut implementation, which is implemented only
-    // if the coroutine-closure has no captures.
+    // We adjust the `self_local` to be a deref since we want to copy fields out of
+    // a reference to the closure.
     if receiver_by_ref {
-        // Triple-check that there's no captures here.
-        assert_eq!(args.as_coroutine_closure().tupled_upvars_ty(), tcx.types.unit);
-        self_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, self_ty);
+        self_local = tcx.mk_place_deref(self_local);
+        self_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, self_ty);
     }
 
     let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
@@ -1067,11 +1064,27 @@ fn build_construct_coroutine_by_move_shim<'tcx>(
         fields.push(Operand::Move(Local::from_usize(idx + 1).into()));
     }
     for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() {
-        fields.push(Operand::Move(tcx.mk_place_field(
-            Local::from_usize(1).into(),
-            FieldIdx::from_usize(idx),
-            ty,
-        )));
+        if receiver_by_ref {
+            // The only situation where it's possible is when we capture immuatable references,
+            // since those don't need to be reborrowed with the closure's env lifetime. Since
+            // references are always `Copy`, just emit a copy.
+            assert_matches!(
+                ty.kind(),
+                ty::Ref(_, _, hir::Mutability::Not),
+                "field should be captured by immutable ref if we have an `Fn` instance"
+            );
+            fields.push(Operand::Copy(tcx.mk_place_field(
+                self_local,
+                FieldIdx::from_usize(idx),
+                ty,
+            )));
+        } else {
+            fields.push(Operand::Move(tcx.mk_place_field(
+                self_local,
+                FieldIdx::from_usize(idx),
+                ty,
+            )));
+        }
     }
 
     let source_info = SourceInfo::outermost(span);
diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs
index 2018a8fe667..ab5c25c4937 100644
--- a/compiler/rustc_mir_transform/src/validate.rs
+++ b/compiler/rustc_mir_transform/src/validate.rs
@@ -400,40 +400,44 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
                 self.check_edge(location, *target, EdgeKind::Normal);
                 self.check_unwind_edge(location, *unwind);
             }
-            TerminatorKind::Call { args, destination, target, unwind, .. } => {
-                if let Some(target) = target {
-                    self.check_edge(location, *target, EdgeKind::Normal);
-                }
-                self.check_unwind_edge(location, *unwind);
+            TerminatorKind::Call { args, .. } | TerminatorKind::TailCall { args, .. } => {
+                // FIXME(explicit_tail_calls): refactor this & add tail-call specific checks
+                if let TerminatorKind::Call { target, unwind, destination, .. } = terminator.kind {
+                    if let Some(target) = target {
+                        self.check_edge(location, target, EdgeKind::Normal);
+                    }
+                    self.check_unwind_edge(location, unwind);
+
+                    // The code generation assumes that there are no critical call edges. The assumption
+                    // is used to simplify inserting code that should be executed along the return edge
+                    // from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
+                    // the code generation should be responsible for handling it.
+                    if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
+                        && self.is_critical_call_edge(target, unwind)
+                    {
+                        self.fail(
+                            location,
+                            format!(
+                                "encountered critical edge in `Call` terminator {:?}",
+                                terminator.kind,
+                            ),
+                        );
+                    }
 
-                // The code generation assumes that there are no critical call edges. The assumption
-                // is used to simplify inserting code that should be executed along the return edge
-                // from the call. FIXME(tmiasko): Since this is a strictly code generation concern,
-                // the code generation should be responsible for handling it.
-                if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Optimized)
-                    && self.is_critical_call_edge(*target, *unwind)
-                {
-                    self.fail(
-                        location,
-                        format!(
-                            "encountered critical edge in `Call` terminator {:?}",
-                            terminator.kind,
-                        ),
-                    );
+                    // The call destination place and Operand::Move place used as an argument might be
+                    // 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(
+                            location,
+                            format!(
+                                "encountered packed place in `Call` terminator destination: {:?}",
+                                terminator.kind,
+                            ),
+                        );
+                    }
                 }
 
-                // The call destination place and Operand::Move place used as an argument might be
-                // 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(
-                        location,
-                        format!(
-                            "encountered packed place in `Call` terminator destination: {:?}",
-                            terminator.kind,
-                        ),
-                    );
-                }
                 for arg in args {
                     if let Operand::Move(place) = &arg.node {
                         if is_within_packed(self.tcx, &self.body.local_decls, *place).is_some() {
@@ -1498,15 +1502,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     }
                 }
             }
-            TerminatorKind::Call { func, .. } => {
+            TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => {
                 let func_ty = func.ty(&self.body.local_decls, self.tcx);
                 match func_ty.kind() {
                     ty::FnPtr(..) | ty::FnDef(..) => {}
                     _ => self.fail(
                         location,
-                        format!("encountered non-callable type {func_ty} in `Call` terminator"),
+                        format!(
+                            "encountered non-callable type {func_ty} in `{}` terminator",
+                            terminator.kind.name()
+                        ),
                     ),
                 }
+
+                if let TerminatorKind::TailCall { .. } = terminator.kind {
+                    // FIXME(explicit_tail_calls): implement tail-call specific checks here (such as signature matching, forbidding closures, etc)
+                }
             }
             TerminatorKind::Assert { cond, .. } => {
                 let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl
index 94b553a07a7..7210701d482 100644
--- a/compiler/rustc_monomorphize/messages.ftl
+++ b/compiler/rustc_monomorphize/messages.ftl
@@ -1,6 +1,3 @@
-monomorphize_consider_type_length_limit =
-    consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate
-
 monomorphize_couldnt_dump_mono_stats =
     unexpected error occurred while dumping monomorphization stats: {$error}
 
@@ -25,8 +22,6 @@ monomorphize_start_not_found = using `fn main` requires the standard library
 
 monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
 
-monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
-
 monomorphize_unknown_cgu_collection_mode =
     unknown codegen-item collection mode '{$mode}', falling back to 'lazy' mode
 
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 235743fccc8..bfd505c0672 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -222,12 +222,12 @@ use rustc_middle::mir::{self, Location, MentionedItem};
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::adjustment::{CustomCoerceUnsized, PointerCoercion};
 use rustc_middle::ty::layout::ValidityRequirement;
-use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::print::{shrunk_instance_name, with_no_trimmed_paths};
+use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{
     self, AssocKind, GenericParamDefKind, Instance, InstanceKind, Ty, TyCtxt, TypeFoldable,
     TypeVisitableExt, VtblEntry,
 };
-use rustc_middle::ty::{GenericArgKind, GenericArgs};
 use rustc_middle::{bug, span_bug};
 use rustc_session::config::EntryFnType;
 use rustc_session::Limit;
@@ -238,9 +238,7 @@ use rustc_target::abi::Size;
 use std::path::PathBuf;
 use tracing::{debug, instrument, trace};
 
-use crate::errors::{
-    self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit, TypeLengthLimit,
-};
+use crate::errors::{self, EncounteredErrorWhileInstantiating, NoOptimizedMir, RecursionLimit};
 use move_check::MoveCheckState;
 
 #[derive(PartialEq)]
@@ -443,7 +441,6 @@ fn collect_items_rec<'tcx>(
                 recursion_depths,
                 recursion_limit,
             ));
-            check_type_length_limit(tcx, instance);
 
             rustc_data_structures::stack::ensure_sufficient_stack(|| {
                 collect_items_of_instance(
@@ -554,34 +551,6 @@ fn collect_items_rec<'tcx>(
     }
 }
 
-/// Format instance name that is already known to be too long for rustc.
-/// Show only the first 2 types if it is longer than 32 characters to avoid blasting
-/// the user's terminal with thousands of lines of type-name.
-///
-/// If the type name is longer than before+after, it will be written to a file.
-fn shrunk_instance_name<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    instance: Instance<'tcx>,
-) -> (String, Option<PathBuf>) {
-    let s = instance.to_string();
-
-    // Only use the shrunk version if it's really shorter.
-    // This also avoids the case where before and after slices overlap.
-    if s.chars().nth(33).is_some() {
-        let shrunk = format!("{}", ty::ShortInstance(instance, 4));
-        if shrunk == s {
-            return (s, None);
-        }
-
-        let path = tcx.output_filenames(()).temp_path_ext("long-type.txt", None);
-        let written_to_path = std::fs::write(&path, s).ok().map(|_| path);
-
-        (shrunk, written_to_path)
-    } else {
-        (s, None)
-    }
-}
-
 fn check_recursion_limit<'tcx>(
     tcx: TyCtxt<'tcx>,
     instance: Instance<'tcx>,
@@ -630,38 +599,6 @@ fn check_recursion_limit<'tcx>(
     (def_id, recursion_depth)
 }
 
-fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
-    let type_length = instance
-        .args
-        .iter()
-        .flat_map(|arg| arg.walk())
-        .filter(|arg| match arg.unpack() {
-            GenericArgKind::Type(_) | GenericArgKind::Const(_) => true,
-            GenericArgKind::Lifetime(_) => false,
-        })
-        .count();
-    debug!(" => type length={}", type_length);
-
-    // Rust code can easily create exponentially-long types using only a
-    // polynomial recursion depth. Even with the default recursion
-    // depth, you can easily get cases that take >2^60 steps to run,
-    // which means that rustc basically hangs.
-    //
-    // Bail out in these cases to avoid that bad user experience.
-    if !tcx.type_length_limit().value_within_limit(type_length) {
-        let (shrunk, written_to_path) = shrunk_instance_name(tcx, instance);
-        let span = tcx.def_span(instance.def_id());
-        let mut path = PathBuf::new();
-        let was_written = if let Some(path2) = written_to_path {
-            path = path2;
-            Some(())
-        } else {
-            None
-        };
-        tcx.dcx().emit_fatal(TypeLengthLimit { span, shrunk, was_written, path, type_length });
-    }
-}
-
 struct MirUsedCollector<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     body: &'a mir::Body<'tcx>,
@@ -818,7 +755,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirUsedCollector<'a, 'tcx> {
         };
 
         match terminator.kind {
-            mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. } => {
+            mir::TerminatorKind::Call { ref func, ref args, ref fn_span, .. }
+            | mir::TerminatorKind::TailCall { ref func, ref args, ref fn_span } => {
                 let callee_ty = func.ty(self.body, tcx);
                 // *Before* monomorphizing, record that we already handled this mention.
                 self.used_mentioned_items.insert(MentionedItem::Fn(callee_ty));
@@ -916,7 +854,7 @@ fn visit_fn_use<'tcx>(
 ) {
     if let ty::FnDef(def_id, args) = *ty.kind() {
         let instance = if is_direct_call {
-            ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args)
+            ty::Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, source)
         } else {
             match ty::Instance::resolve_for_fn_ptr(tcx, ty::ParamEnv::reveal_all(), def_id, args) {
                 Some(instance) => instance,
@@ -1223,10 +1161,10 @@ fn collect_alloc<'tcx>(tcx: TyCtxt<'tcx>, alloc_id: AllocId, output: &mut MonoIt
                 });
             }
         }
-        GlobalAlloc::Function(fn_instance) => {
-            if should_codegen_locally(tcx, fn_instance) {
-                trace!("collecting {:?} with {:#?}", alloc_id, fn_instance);
-                output.push(create_fn_mono_item(tcx, fn_instance, DUMMY_SP));
+        GlobalAlloc::Function { instance, .. } => {
+            if should_codegen_locally(tcx, instance) {
+                trace!("collecting {:?} with {:#?}", alloc_id, instance);
+                output.push(create_fn_mono_item(tcx, instance, DUMMY_SP));
             }
         }
         GlobalAlloc::VTable(ty, trait_ref) => {
@@ -1319,7 +1257,7 @@ fn visit_mentioned_item<'tcx>(
         MentionedItem::Fn(ty) => {
             if let ty::FnDef(def_id, args) = *ty.kind() {
                 let instance =
-                    Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args);
+                    Instance::expect_resolve(tcx, ty::ParamEnv::reveal_all(), def_id, args, span);
                 // `visit_instance_use` was written for "used" item collection but works just as well
                 // for "mentioned" item collection.
                 // We can set `is_direct_call`; that just means we'll skip a bunch of shims that anyway
@@ -1544,6 +1482,7 @@ impl<'v> RootCollector<'_, 'v> {
             ty::ParamEnv::reveal_all(),
             start_def_id,
             self.tcx.mk_args(&[main_ret_ty.into()]),
+            DUMMY_SP,
         );
 
         self.output.push(create_fn_mono_item(self.tcx, start_instance, DUMMY_SP));
@@ -1612,9 +1551,10 @@ fn create_mono_items_for_default_impls<'tcx>(
         }
 
         // As mentioned above, the method is legal to eagerly instantiate if it
-        // only has lifetime generic parameters. This is validated by
+        // only has lifetime generic parameters. This is validated by calling
+        // `own_requires_monomorphization` on both the impl and method.
         let args = trait_ref.args.extend_to(tcx, method.def_id, only_region_params);
-        let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args);
+        let instance = ty::Instance::expect_resolve(tcx, param_env, method.def_id, args, DUMMY_SP);
 
         let mono_item = create_fn_mono_item(tcx, instance, DUMMY_SP);
         if mono_item.node.is_instantiable(tcx) && should_codegen_locally(tcx, instance) {
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index c0d1efd96c5..9548c46e6fa 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -20,19 +20,6 @@ pub struct RecursionLimit {
 }
 
 #[derive(Diagnostic)]
-#[diag(monomorphize_type_length_limit)]
-#[help(monomorphize_consider_type_length_limit)]
-pub struct TypeLengthLimit {
-    #[primary_span]
-    pub span: Span,
-    pub shrunk: String,
-    #[note(monomorphize_written_to_path)]
-    pub was_written: Option<()>,
-    pub path: PathBuf,
-    pub type_length: usize,
-}
-
-#[derive(Diagnostic)]
 #[diag(monomorphize_no_optimized_mir)]
 pub struct NoOptimizedMir {
     #[note]
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 695d02705ab..82488088e30 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -4,7 +4,8 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::visit::TypeVisitableExt;
 use rustc_type_ir::{
-    self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Interner,
+    self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, InferCtxtLike,
+    Interner,
 };
 
 use crate::delegate::SolverDelegate;
diff --git a/compiler/rustc_next_trait_solver/src/coherence.rs b/compiler/rustc_next_trait_solver/src/coherence.rs
new file mode 100644
index 00000000000..55f602d907b
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/coherence.rs
@@ -0,0 +1,469 @@
+use std::fmt::Debug;
+use std::ops::ControlFlow;
+
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor};
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
+use tracing::instrument;
+
+/// Whether we do the orphan check relative to this crate or to some remote crate.
+#[derive(Copy, Clone, Debug)]
+pub enum InCrate {
+    Local { mode: OrphanCheckMode },
+    Remote,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum OrphanCheckMode {
+    /// Proper orphan check.
+    Proper,
+    /// Improper orphan check for backward compatibility.
+    ///
+    /// In this mode, type params inside projections are considered to be covered
+    /// even if the projection may normalize to a type that doesn't actually cover
+    /// them. This is unsound. See also [#124559] and [#99554].
+    ///
+    /// [#124559]: https://github.com/rust-lang/rust/issues/124559
+    /// [#99554]: https://github.com/rust-lang/rust/issues/99554
+    Compat,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum Conflict {
+    Upstream,
+    Downstream,
+}
+
+/// Returns whether all impls which would apply to the `trait_ref`
+/// e.g. `Ty: Trait<Arg>` are already known in the local crate.
+///
+/// This both checks whether any downstream or sibling crates could
+/// implement it and whether an upstream crate can add this impl
+/// without breaking backwards compatibility.
+#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)]
+pub fn trait_ref_is_knowable<Infcx, I, E>(
+    infcx: &Infcx,
+    trait_ref: ty::TraitRef<I>,
+    mut lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>,
+) -> Result<Result<(), Conflict>, E>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    E: Debug,
+{
+    if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok()
+    {
+        // A downstream or cousin crate is allowed to implement some
+        // generic parameters of this trait-ref.
+        return Ok(Err(Conflict::Downstream));
+    }
+
+    if trait_ref_is_local_or_fundamental(infcx.cx(), trait_ref) {
+        // This is a local or fundamental trait, so future-compatibility
+        // is no concern. We know that downstream/cousin crates are not
+        // allowed to implement a generic parameter of this trait ref,
+        // which means impls could only come from dependencies of this
+        // crate, which we already know about.
+        return Ok(Ok(()));
+    }
+
+    // This is a remote non-fundamental trait, so if another crate
+    // can be the "final owner" of the generic parameters of this trait-ref,
+    // they are allowed to implement it future-compatibly.
+    //
+    // However, if we are a final owner, then nobody else can be,
+    // and if we are an intermediate owner, then we don't care
+    // about future-compatibility, which means that we're OK if
+    // we are an owner.
+    if orphan_check_trait_ref(
+        infcx,
+        trait_ref,
+        InCrate::Local { mode: OrphanCheckMode::Proper },
+        &mut lazily_normalize_ty,
+    )?
+    .is_ok()
+    {
+        Ok(Ok(()))
+    } else {
+        Ok(Err(Conflict::Upstream))
+    }
+}
+
+pub fn trait_ref_is_local_or_fundamental<I: Interner>(tcx: I, trait_ref: ty::TraitRef<I>) -> bool {
+    trait_ref.def_id.is_local() || tcx.trait_is_fundamental(trait_ref.def_id)
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum IsFirstInputType {
+    No,
+    Yes,
+}
+
+impl From<bool> for IsFirstInputType {
+    fn from(b: bool) -> IsFirstInputType {
+        match b {
+            false => IsFirstInputType::No,
+            true => IsFirstInputType::Yes,
+        }
+    }
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = "T: Debug"))]
+pub enum OrphanCheckErr<I: Interner, T> {
+    NonLocalInputType(Vec<(I::Ty, IsFirstInputType)>),
+    UncoveredTyParams(UncoveredTyParams<I, T>),
+}
+
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = "T: Debug"))]
+pub struct UncoveredTyParams<I: Interner, T> {
+    pub uncovered: T,
+    pub local_ty: Option<I::Ty>,
+}
+
+/// Checks whether a trait-ref is potentially implementable by a crate.
+///
+/// The current rule is that a trait-ref orphan checks in a crate C:
+///
+/// 1. Order the parameters in the trait-ref in generic parameters order
+/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W).
+/// 2. Of these type parameters, there is at least one type parameter
+///    in which, walking the type as a tree, you can reach a type local
+///    to C where all types in-between are fundamental types. Call the
+///    first such parameter the "local key parameter".
+///     - e.g., `Box<LocalType>` is OK, because you can visit LocalType
+///       going through `Box`, which is fundamental.
+///     - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for
+///       the same reason.
+///     - but (knowing that `Vec<T>` is non-fundamental, and assuming it's
+///       not local), `Vec<LocalType>` is bad, because `Vec<->` is between
+///       the local type and the type parameter.
+/// 3. Before this local type, no generic type parameter of the impl must
+///    be reachable through fundamental types.
+///     - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental.
+///     - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is
+///       reachable through the fundamental type `Box`.
+/// 4. Every type in the local key parameter not known in C, going
+///    through the parameter's type tree, must appear only as a subtree of
+///    a type local to C, with only fundamental types between the type
+///    local to C and the local key parameter.
+///     - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`)
+///     is bad, because the only local type with `T` as a subtree is
+///     `LocalType<T>`, and `Vec<->` is between it and the type parameter.
+///     - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because
+///     the second occurrence of `T` is not a subtree of *any* local type.
+///     - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of
+///     `LocalType<Vec<T>>`, which is local and has no types between it and
+///     the type parameter.
+///
+/// The orphan rules actually serve several different purposes:
+///
+/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where
+///    every type local to one crate is unknown in the other) can't implement
+///    the same trait-ref. This follows because it can be seen that no such
+///    type can orphan-check in 2 such crates.
+///
+///    To check that a local impl follows the orphan rules, we check it in
+///    InCrate::Local mode, using type parameters for the "generic" types.
+///
+///    In InCrate::Local mode the orphan check succeeds if the current crate
+///    is definitely allowed to implement the given trait (no false positives).
+///
+/// 2. They ground negative reasoning for coherence. If a user wants to
+///    write both a conditional blanket impl and a specific impl, we need to
+///    make sure they do not overlap. For example, if we write
+///    ```ignore (illustrative)
+///    impl<T> IntoIterator for Vec<T>
+///    impl<T: Iterator> IntoIterator for T
+///    ```
+///    We need to be able to prove that `Vec<$0>: !Iterator` for every type $0.
+///    We can observe that this holds in the current crate, but we need to make
+///    sure this will also hold in all unknown crates (both "independent" crates,
+///    which we need for link-safety, and also child crates, because we don't want
+///    child crates to get error for impl conflicts in a *dependency*).
+///
+///    For that, we only allow negative reasoning if, for every assignment to the
+///    inference variables, every unknown crate would get an orphan error if they
+///    try to implement this trait-ref. To check for this, we use InCrate::Remote
+///    mode. That is sound because we already know all the impls from known crates.
+///
+///    In InCrate::Remote mode the orphan check succeeds if a foreign crate
+///    *could* implement the given trait (no false negatives).
+///
+/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
+///    add "non-blanket" impls without breaking negative reasoning in dependent
+///    crates. This is the "rebalancing coherence" (RFC 1023) restriction.
+///
+///    For that, we only allow a crate to perform negative reasoning on
+///    non-local-non-`#[fundamental]` if there's a local key parameter as per (2).
+///
+///    Because we never perform negative reasoning generically (coherence does
+///    not involve type parameters), this can be interpreted as doing the full
+///    orphan check (using InCrate::Local mode), instantiating non-local known
+///    types for all inference variables.
+///
+///    This allows for crates to future-compatibly add impls as long as they
+///    can't apply to types with a key parameter in a child crate - applying
+///    the rules, this basically means that every type parameter in the impl
+///    must appear behind a non-fundamental type (because this is not a
+///    type-system requirement, crate owners might also go for "semantic
+///    future-compatibility" involving things such as sealed traits, but
+///    the above requirement is sufficient, and is necessary in "open world"
+///    cases).
+///
+/// Note that this function is never called for types that have both type
+/// parameters and inference variables.
+#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)]
+pub fn orphan_check_trait_ref<Infcx, I, E: Debug>(
+    infcx: &Infcx,
+    trait_ref: ty::TraitRef<I>,
+    in_crate: InCrate,
+    lazily_normalize_ty: impl FnMut(I::Ty) -> Result<I::Ty, E>,
+) -> Result<Result<(), OrphanCheckErr<I, I::Ty>>, E>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    E: Debug,
+{
+    if trait_ref.has_param() {
+        panic!("orphan check only expects inference variables: {trait_ref:?}");
+    }
+
+    let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty);
+    Ok(match trait_ref.visit_with(&mut checker) {
+        ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
+        ControlFlow::Break(residual) => match residual {
+            OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err),
+            OrphanCheckEarlyExit::UncoveredTyParam(ty) => {
+                // Does there exist some local type after the `ParamTy`.
+                checker.search_first_local_ty = true;
+                let local_ty = match trait_ref.visit_with(&mut checker) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty),
+                    _ => None,
+                };
+                Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
+                    uncovered: ty,
+                    local_ty,
+                }))
+            }
+            OrphanCheckEarlyExit::LocalTy(_) => Ok(()),
+        },
+    })
+}
+
+struct OrphanChecker<'a, Infcx, I: Interner, F> {
+    infcx: &'a Infcx,
+    in_crate: InCrate,
+    in_self_ty: bool,
+    lazily_normalize_ty: F,
+    /// Ignore orphan check failures and exclusively search for the first local type.
+    search_first_local_ty: bool,
+    non_local_tys: Vec<(I::Ty, IsFirstInputType)>,
+}
+
+impl<'a, Infcx, I, F, E> OrphanChecker<'a, Infcx, I, F>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    F: FnOnce(I::Ty) -> Result<I::Ty, E>,
+{
+    fn new(infcx: &'a Infcx, in_crate: InCrate, lazily_normalize_ty: F) -> Self {
+        OrphanChecker {
+            infcx,
+            in_crate,
+            in_self_ty: true,
+            lazily_normalize_ty,
+            search_first_local_ty: false,
+            non_local_tys: Vec::new(),
+        }
+    }
+
+    fn found_non_local_ty(&mut self, t: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> {
+        self.non_local_tys.push((t, self.in_self_ty.into()));
+        ControlFlow::Continue(())
+    }
+
+    fn found_uncovered_ty_param(&mut self, ty: I::Ty) -> ControlFlow<OrphanCheckEarlyExit<I, E>> {
+        if self.search_first_local_ty {
+            return ControlFlow::Continue(());
+        }
+
+        ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty))
+    }
+
+    fn def_id_is_local(&mut self, def_id: I::DefId) -> bool {
+        match self.in_crate {
+            InCrate::Local { .. } => def_id.is_local(),
+            InCrate::Remote => false,
+        }
+    }
+}
+
+enum OrphanCheckEarlyExit<I: Interner, E> {
+    NormalizationFailure(E),
+    UncoveredTyParam(I::Ty),
+    LocalTy(I::Ty),
+}
+
+impl<'a, Infcx, I, F, E> TypeVisitor<I> for OrphanChecker<'a, Infcx, I, F>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+    F: FnMut(I::Ty) -> Result<I::Ty, E>,
+{
+    type Result = ControlFlow<OrphanCheckEarlyExit<I, E>>;
+
+    fn visit_region(&mut self, _r: I::Region) -> Self::Result {
+        ControlFlow::Continue(())
+    }
+
+    fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
+        let ty = self.infcx.shallow_resolve(ty);
+        let ty = match (self.lazily_normalize_ty)(ty) {
+            Ok(norm_ty) if norm_ty.is_ty_var() => ty,
+            Ok(norm_ty) => norm_ty,
+            Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
+        };
+
+        let result = match ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(..)
+            | ty::Uint(..)
+            | ty::Float(..)
+            | ty::Str
+            | ty::FnDef(..)
+            | ty::Pat(..)
+            | ty::FnPtr(_)
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Never
+            | ty::Tuple(..) => self.found_non_local_ty(ty),
+
+            ty::Param(..) => panic!("unexpected ty param"),
+
+            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => {
+                match self.in_crate {
+                    InCrate::Local { .. } => self.found_uncovered_ty_param(ty),
+                    // The inference variable might be unified with a local
+                    // type in that remote crate.
+                    InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+                }
+            }
+
+            // A rigid alias may normalize to anything.
+            // * If it references an infer var, placeholder or bound ty, it may
+            //   normalize to that, so we have to treat it as an uncovered ty param.
+            // * Otherwise it may normalize to any non-type-generic type
+            //   be it local or non-local.
+            ty::Alias(kind, _) => {
+                if ty.has_type_flags(
+                    ty::TypeFlags::HAS_TY_PLACEHOLDER
+                        | ty::TypeFlags::HAS_TY_BOUND
+                        | ty::TypeFlags::HAS_TY_INFER,
+                ) {
+                    match self.in_crate {
+                        InCrate::Local { mode } => match kind {
+                            ty::Projection => {
+                                if let OrphanCheckMode::Compat = mode {
+                                    ControlFlow::Continue(())
+                                } else {
+                                    self.found_uncovered_ty_param(ty)
+                                }
+                            }
+                            _ => self.found_uncovered_ty_param(ty),
+                        },
+                        InCrate::Remote => {
+                            // The inference variable might be unified with a local
+                            // type in that remote crate.
+                            ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                        }
+                    }
+                } else {
+                    // Regarding *opaque types* specifically, we choose to treat them as non-local,
+                    // even those that appear within the same crate. This seems somewhat surprising
+                    // at first, but makes sense when you consider that opaque types are supposed
+                    // to hide the underlying type *within the same crate*. When an opaque type is
+                    // used from outside the module where it is declared, it should be impossible to
+                    // observe anything about it other than the traits that it implements.
+                    //
+                    // The alternative would be to look at the underlying type to determine whether
+                    // or not the opaque type itself should be considered local.
+                    //
+                    // However, this could make it a breaking change to switch the underlying hidden
+                    // type from a local type to a remote type. This would violate the rule that
+                    // opaque types should be completely opaque apart from the traits that they
+                    // implement, so we don't use this behavior.
+                    // Addendum: Moreover, revealing the underlying type is likely to cause cycle
+                    // errors as we rely on coherence / the specialization graph during typeck.
+
+                    self.found_non_local_ty(ty)
+                }
+            }
+
+            // For fundamental types, we just look inside of them.
+            ty::Ref(_, ty, _) => ty.visit_with(self),
+            ty::Adt(def, args) => {
+                if self.def_id_is_local(def.def_id()) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else if def.is_fundamental() {
+                    args.visit_with(self)
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Foreign(def_id) => {
+                if self.def_id_is_local(def_id) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Dynamic(tt, ..) => {
+                let principal = tt.principal().map(|p| p.def_id());
+                if principal.is_some_and(|p| self.def_id_is_local(p)) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+            ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => {
+                if self.def_id_is_local(did) {
+                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
+                } else {
+                    self.found_non_local_ty(ty)
+                }
+            }
+            // This should only be created when checking whether we have to check whether some
+            // auto trait impl applies. There will never be multiple impls, so we can just
+            // act as if it were a local type here.
+            ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
+        };
+        // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
+        // the first type we visit is always the self type.
+        self.in_self_ty = false;
+        result
+    }
+
+    /// All possible values for a constant parameter already exist
+    /// in the crate defining the trait, so they are always non-local[^1].
+    ///
+    /// Because there's no way to have an impl where the first local
+    /// generic argument is a constant, we also don't have to fail
+    /// the orphan check when encountering a parameter or a generic constant.
+    ///
+    /// This means that we can completely ignore constants during the orphan check.
+    ///
+    /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples.
+    ///
+    /// [^1]: This might not hold for function pointers or trait objects in the future.
+    /// As these should be quite rare as const arguments and especially rare as impl
+    /// parameters, allowing uncovered const parameters in impls seems more useful
+    /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
+    fn visit_const(&mut self, _c: I::Const) -> Self::Result {
+        ControlFlow::Continue(())
+    }
+}
diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs
index 6626acfe963..6a3d58b5906 100644
--- a/compiler/rustc_next_trait_solver/src/delegate.rs
+++ b/compiler/rustc_next_trait_solver/src/delegate.rs
@@ -1,18 +1,19 @@
-use std::fmt::Debug;
+use std::ops::Deref;
 
 use rustc_type_ir::fold::TypeFoldable;
-use rustc_type_ir::relate::Relate;
 use rustc_type_ir::solve::{Certainty, Goal, NoSolution, SolverMode};
-use rustc_type_ir::{self as ty, Interner};
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 
-pub trait SolverDelegate: Sized {
+pub trait SolverDelegate:
+    Deref<Target: InferCtxtLike<Interner = <Self as SolverDelegate>::Interner>> + Sized
+{
     type Interner: Interner;
-    fn cx(&self) -> Self::Interner;
+    fn cx(&self) -> Self::Interner {
+        (**self).cx()
+    }
 
     type Span: Copy;
 
-    fn solver_mode(&self) -> SolverMode;
-
     fn build_with_canonical<V>(
         cx: Self::Interner,
         solver_mode: SolverMode,
@@ -21,109 +22,21 @@ pub trait SolverDelegate: Sized {
     where
         V: TypeFoldable<Self::Interner>;
 
-    fn universe(&self) -> ty::UniverseIndex;
-    fn create_next_universe(&self) -> ty::UniverseIndex;
-
-    fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>;
-    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>;
-    fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>;
-
-    fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid;
-    fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid;
-
-    fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_float_var(
-        &self,
-        vid: ty::FloatVid,
-    ) -> <Self::Interner as Interner>::Ty;
-    fn opportunistic_resolve_ct_var(
-        &self,
-        vid: ty::ConstVid,
-    ) -> <Self::Interner as Interner>::Const;
-    fn opportunistic_resolve_effect_var(
-        &self,
-        vid: ty::EffectVid,
-    ) -> <Self::Interner as Interner>::Const;
-    fn opportunistic_resolve_lt_var(
-        &self,
-        vid: ty::RegionVid,
-    ) -> <Self::Interner as Interner>::Region;
-
-    fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes;
-
-    fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty;
-    fn next_const_infer(&self) -> <Self::Interner as Interner>::Const;
-    fn fresh_args_for_item(
-        &self,
-        def_id: <Self::Interner as Interner>::DefId,
-    ) -> <Self::Interner as Interner>::GenericArgs;
-
     fn fresh_var_for_kind_with_span(
         &self,
         arg: <Self::Interner as Interner>::GenericArg,
         span: Self::Span,
     ) -> <Self::Interner as Interner>::GenericArg;
 
-    fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>(
-        &self,
-        value: ty::Binder<Self::Interner, T>,
-    ) -> T;
-
-    fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>(
-        &self,
-        value: ty::Binder<Self::Interner, T>,
-        f: impl FnOnce(T) -> U,
-    ) -> U;
-
-    fn relate<T: Relate<Self::Interner>>(
-        &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
-
-    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
-        &self,
-        param_env: <Self::Interner as Interner>::ParamEnv,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
-
-    fn resolve_vars_if_possible<T>(&self, value: T) -> T
-    where
-        T: TypeFoldable<Self::Interner>;
-
-    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T;
-
     // FIXME: Uplift the leak check into this crate.
     fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution>;
 
-    // FIXME: This is only here because elaboration lives in `rustc_infer`!
-    fn elaborate_supertraits(
-        cx: Self::Interner,
-        trait_ref: ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>,
-    ) -> impl Iterator<Item = ty::Binder<Self::Interner, ty::TraitRef<Self::Interner>>>;
-
     fn try_const_eval_resolve(
         &self,
         param_env: <Self::Interner as Interner>::ParamEnv,
         unevaluated: ty::UnevaluatedConst<Self::Interner>,
     ) -> Option<<Self::Interner as Interner>::Const>;
 
-    fn sub_regions(
-        &self,
-        sub: <Self::Interner as Interner>::Region,
-        sup: <Self::Interner as Interner>::Region,
-    );
-
-    fn register_ty_outlives(
-        &self,
-        ty: <Self::Interner as Interner>::Ty,
-        r: <Self::Interner as Interner>::Region,
-    );
-
     // FIXME: This only is here because `wf::obligations` is in `rustc_trait_selection`!
     fn well_formed_goals(
         &self,
@@ -179,14 +92,6 @@ pub trait SolverDelegate: Sized {
 
     fn reset_opaque_types(&self);
 
-    fn trait_ref_is_knowable<E: Debug>(
-        &self,
-        trait_ref: ty::TraitRef<Self::Interner>,
-        lazily_normalize_ty: impl FnMut(
-            <Self::Interner as Interner>::Ty,
-        ) -> Result<<Self::Interner as Interner>::Ty, E>,
-    ) -> Result<bool, E>;
-
     fn fetch_eligible_assoc_item(
         &self,
         param_env: <Self::Interner as Interner>::ParamEnv,
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index a6a9c01faaa..0a5b4278058 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -5,6 +5,8 @@
 //! So if you got to this crate from the old solver, it's totally normal.
 
 pub mod canonicalizer;
+pub mod coherence;
 pub mod delegate;
+pub mod relate;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/relate.rs b/compiler/rustc_next_trait_solver/src/relate.rs
new file mode 100644
index 00000000000..db819961bbd
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/relate.rs
@@ -0,0 +1,15 @@
+pub use rustc_type_ir::relate::*;
+
+pub mod combine;
+
+/// Whether aliases should be related structurally or not. Used
+/// to adjust the behavior of generalization and combine.
+///
+/// This should always be `No` unless in a few special-cases when
+/// instantiating canonical responses and in the new solver. Each
+/// such case should have a comment explaining why it is used.
+#[derive(Debug, Copy, Clone)]
+pub enum StructurallyRelateAliases {
+    Yes,
+    No,
+}
diff --git a/compiler/rustc_next_trait_solver/src/relate/combine.rs b/compiler/rustc_next_trait_solver/src/relate/combine.rs
new file mode 100644
index 00000000000..96968327d8e
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/relate/combine.rs
@@ -0,0 +1,34 @@
+pub use rustc_type_ir::relate::*;
+use rustc_type_ir::solve::Goal;
+use rustc_type_ir::{InferCtxtLike, Interner, Upcast};
+
+use super::StructurallyRelateAliases;
+
+pub trait PredicateEmittingRelation<Infcx, I = <Infcx as InferCtxtLike>::Interner>:
+    TypeRelation<I>
+where
+    Infcx: InferCtxtLike<Interner = I>,
+    I: Interner,
+{
+    fn span(&self) -> I::Span;
+
+    fn param_env(&self) -> I::ParamEnv;
+
+    /// Whether aliases should be related structurally. This is pretty much
+    /// always `No` unless you're equating in some specific locations of the
+    /// new solver. See the comments in these use-cases for more details.
+    fn structurally_relate_aliases(&self) -> StructurallyRelateAliases;
+
+    /// Register obligations that must hold in order for this relation to hold
+    fn register_goals(&mut self, obligations: impl IntoIterator<Item = Goal<I, I::Predicate>>);
+
+    /// Register predicates that must hold in order for this relation to hold.
+    /// This uses the default `param_env` of the obligation.
+    fn register_predicates(
+        &mut self,
+        obligations: impl IntoIterator<Item: Upcast<I, I::Predicate>>,
+    );
+
+    /// Register `AliasRelate` obligation(s) that both types must be related to each other.
+    fn register_alias_relate_predicate(&mut self, a: I::Ty, b: I::Ty);
+}
diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs
index 6ed58d0e4fb..254ee514f8b 100644
--- a/compiler/rustc_next_trait_solver/src/resolve.rs
+++ b/compiler/rustc_next_trait_solver/src/resolve.rs
@@ -2,7 +2,7 @@ use crate::delegate::SolverDelegate;
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::visit::TypeVisitableExt;
-use rustc_type_ir::{self as ty, Interner};
+use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 
 ///////////////////////////////////////////////////////////////////////////
 // EAGER RESOLUTION
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 21439530c08..01dde9ca587 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -2,6 +2,7 @@
 
 pub(super) mod structural_traits;
 
+use rustc_type_ir::elaborate;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
@@ -269,6 +270,11 @@ where
         ecx: &mut EvalCtxt<'_, D>,
         goal: Goal<I, Self>,
     ) -> Vec<Candidate<I>>;
+
+    fn consider_builtin_effects_intersection_candidate(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 }
 
 impl<D, I> EvalCtxt<'_, D>
@@ -382,46 +388,83 @@ where
             G::consider_auto_trait_candidate(self, goal)
         } else if cx.trait_is_alias(trait_def_id) {
             G::consider_trait_alias_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
-            G::consider_builtin_sized_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
-            || cx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
-        {
-            G::consider_builtin_copy_clone_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
-            G::consider_builtin_pointer_like_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
-            G::consider_builtin_fn_ptr_trait_candidate(self, goal)
-        } else if let Some(kind) = self.cx().fn_trait_kind_from_def_id(trait_def_id) {
-            G::consider_builtin_fn_trait_candidates(self, goal, kind)
-        } else if let Some(kind) = self.cx().async_fn_trait_kind_from_def_id(trait_def_id) {
-            G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
-            G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
-            G::consider_builtin_tuple_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
-            G::consider_builtin_pointee_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
-            G::consider_builtin_future_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
-            G::consider_builtin_iterator_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
-            G::consider_builtin_fused_iterator_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
-            G::consider_builtin_async_iterator_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
-            G::consider_builtin_coroutine_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
-            G::consider_builtin_discriminant_kind_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
-            G::consider_builtin_async_destruct_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
-            G::consider_builtin_destruct_candidate(self, goal)
-        } else if cx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
-            G::consider_builtin_transmute_candidate(self, goal)
         } else {
-            Err(NoSolution)
+            match cx.as_lang_item(trait_def_id) {
+                Some(TraitSolverLangItem::Sized) => G::consider_builtin_sized_candidate(self, goal),
+                Some(TraitSolverLangItem::Copy | TraitSolverLangItem::Clone) => {
+                    G::consider_builtin_copy_clone_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Fn) => {
+                    G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::Fn)
+                }
+                Some(TraitSolverLangItem::FnMut) => {
+                    G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnMut)
+                }
+                Some(TraitSolverLangItem::FnOnce) => {
+                    G::consider_builtin_fn_trait_candidates(self, goal, ty::ClosureKind::FnOnce)
+                }
+                Some(TraitSolverLangItem::AsyncFn) => {
+                    G::consider_builtin_async_fn_trait_candidates(self, goal, ty::ClosureKind::Fn)
+                }
+                Some(TraitSolverLangItem::AsyncFnMut) => {
+                    G::consider_builtin_async_fn_trait_candidates(
+                        self,
+                        goal,
+                        ty::ClosureKind::FnMut,
+                    )
+                }
+                Some(TraitSolverLangItem::AsyncFnOnce) => {
+                    G::consider_builtin_async_fn_trait_candidates(
+                        self,
+                        goal,
+                        ty::ClosureKind::FnOnce,
+                    )
+                }
+                Some(TraitSolverLangItem::PointerLike) => {
+                    G::consider_builtin_pointer_like_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::FnPtrTrait) => {
+                    G::consider_builtin_fn_ptr_trait_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::AsyncFnKindHelper) => {
+                    G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Tuple) => G::consider_builtin_tuple_candidate(self, goal),
+                Some(TraitSolverLangItem::PointeeTrait) => {
+                    G::consider_builtin_pointee_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Future) => {
+                    G::consider_builtin_future_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Iterator) => {
+                    G::consider_builtin_iterator_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::FusedIterator) => {
+                    G::consider_builtin_fused_iterator_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::AsyncIterator) => {
+                    G::consider_builtin_async_iterator_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Coroutine) => {
+                    G::consider_builtin_coroutine_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::DiscriminantKind) => {
+                    G::consider_builtin_discriminant_kind_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::AsyncDestruct) => {
+                    G::consider_builtin_async_destruct_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::Destruct) => {
+                    G::consider_builtin_destruct_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::TransmuteTrait) => {
+                    G::consider_builtin_transmute_candidate(self, goal)
+                }
+                Some(TraitSolverLangItem::EffectsIntersection) => {
+                    G::consider_builtin_effects_intersection_candidate(self, goal)
+                }
+                _ => Err(NoSolution),
+            }
         };
 
         candidates.extend(result);
@@ -625,7 +668,7 @@ where
         // a projection goal.
         if let Some(principal) = bounds.principal() {
             let principal_trait_ref = principal.with_self_ty(cx, self_ty);
-            for (idx, assumption) in D::elaborate_supertraits(cx, principal_trait_ref).enumerate() {
+            for (idx, assumption) in elaborate::supertraits(cx, principal_trait_ref).enumerate() {
                 candidates.extend(G::probe_and_consider_object_bound_candidate(
                     self,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index 0cef8d9f4bc..7df14e81ab5 100644
--- a/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -668,8 +668,11 @@ where
 {
     let cx = ecx.cx();
     let mut requirements = vec![];
-    requirements
-        .extend(cx.super_predicates_of(trait_ref.def_id).iter_instantiated(cx, trait_ref.args));
+    requirements.extend(
+        cx.explicit_super_predicates_of(trait_ref.def_id)
+            .iter_instantiated(cx, trait_ref.args)
+            .map(|(pred, _)| pred),
+    );
 
     // FIXME(associated_const_equality): Also add associated consts to
     // the requirements here.
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index 0a313c6a951..9474d501d6f 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -14,7 +14,7 @@ use std::iter;
 use rustc_index::IndexVec;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::inherent::*;
-use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, Interner};
+use rustc_type_ir::{self as ty, Canonical, CanonicalVarValues, InferCtxtLike, Interner};
 use tracing::{instrument, trace};
 
 use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 87342eefb33..c90f8e76163 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -7,10 +7,11 @@ use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::relate::Relate;
 use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
-use rustc_type_ir::{self as ty, CanonicalVarValues, Interner};
+use rustc_type_ir::{self as ty, CanonicalVarValues, InferCtxtLike, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
 use tracing::{instrument, trace};
 
+use crate::coherence;
 use crate::delegate::SolverDelegate;
 use crate::solve::inspect::{self, ProofTreeBuilder};
 use crate::solve::search_graph::SearchGraph;
@@ -906,7 +907,8 @@ where
     ) -> Result<bool, NoSolution> {
         let delegate = self.delegate;
         let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty);
-        delegate.trait_ref_is_knowable(trait_ref, lazily_normalize_ty)
+        coherence::trait_ref_is_knowable(&**delegate, trait_ref, lazily_normalize_ty)
+            .map(|is_knowable| is_knowable.is_ok())
     }
 
     pub(super) fn fetch_eligible_assoc_item(
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
index e9516c60c70..4258dd9263a 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
@@ -1,6 +1,6 @@
 use std::marker::PhantomData;
 
-use rustc_type_ir::Interner;
+use rustc_type_ir::{InferCtxtLike, Interner};
 use tracing::instrument;
 
 use crate::delegate::SolverDelegate;
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 4e8cb4384f4..a83bd689a80 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -3,6 +3,7 @@ mod inherent;
 mod opaque_types;
 mod weak_types;
 
+use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::Upcast as _;
@@ -144,7 +145,7 @@ where
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(cx);
         let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
-        if !ecx.cx().args_may_unify_deep(
+        if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup).args_may_unify(
             goal.predicate.alias.trait_ref(cx).args,
             impl_trait_ref.skip_binder().args,
         ) {
@@ -864,6 +865,68 @@ where
     ) -> Result<Candidate<I>, NoSolution> {
         panic!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
     }
+
+    fn consider_builtin_effects_intersection_candidate(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        let ty::Tuple(types) = goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        let cx = ecx.cx();
+
+        let mut first_non_maybe = None;
+        let mut non_maybe_count = 0;
+        for ty in types.iter() {
+            if !matches!(ty::EffectKind::try_from_ty(cx, ty), Some(ty::EffectKind::Maybe)) {
+                first_non_maybe.get_or_insert(ty);
+                non_maybe_count += 1;
+            }
+        }
+
+        match non_maybe_count {
+            0 => {
+                let ty = ty::EffectKind::Maybe.to_ty(cx);
+                ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
+                    ecx.instantiate_normalizes_to_term(goal, ty.into());
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                })
+            }
+            1 => {
+                let ty = first_non_maybe.unwrap();
+                ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
+                    ecx.instantiate_normalizes_to_term(goal, ty.into());
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                })
+            }
+            _ => {
+                let mut min = ty::EffectKind::Maybe;
+
+                for ty in types.iter() {
+                    // We can't find the intersection if the types used are generic.
+                    //
+                    // FIXME(effects) do we want to look at where clauses to get some
+                    // clue for the case where generic types are being used?
+                    let Some(kind) = ty::EffectKind::try_from_ty(cx, ty) else {
+                        return Err(NoSolution);
+                    };
+
+                    let Some(result) = ty::EffectKind::intersection(min, kind) else {
+                        return Err(NoSolution);
+                    };
+
+                    min = result;
+                }
+
+                let ty = min.to_ty(cx);
+                ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
+                    ecx.instantiate_normalizes_to_term(goal, ty.into());
+                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                })
+            }
+        }
+    }
 }
 
 impl<D, I> EvalCtxt<'_, D>
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 2bc9d35c2b0..4474bbc2351 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -2,10 +2,11 @@
 
 use rustc_ast_ir::Movability;
 use rustc_type_ir::data_structures::IndexSet;
+use rustc_type_ir::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
 use rustc_type_ir::visit::TypeVisitableExt as _;
-use rustc_type_ir::{self as ty, Interner, TraitPredicate, Upcast as _};
+use rustc_type_ir::{self as ty, elaborate, Interner, TraitPredicate, Upcast as _};
 use tracing::{instrument, trace};
 
 use crate::delegate::SolverDelegate;
@@ -46,7 +47,8 @@ where
         let cx = ecx.cx();
 
         let impl_trait_ref = cx.impl_trait_ref(impl_def_id);
-        if !cx.args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
+        if !DeepRejectCtxt::new(ecx.cx(), TreatParams::ForLookup)
+            .args_may_unify(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
         {
             return Err(NoSolution);
         }
@@ -702,6 +704,47 @@ where
             }
         })
     }
+
+    fn consider_builtin_effects_intersection_candidate(
+        ecx: &mut EvalCtxt<'_, D>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        if goal.predicate.polarity != ty::PredicatePolarity::Positive {
+            return Err(NoSolution);
+        }
+
+        let ty::Tuple(types) = goal.predicate.self_ty().kind() else {
+            return Err(NoSolution);
+        };
+
+        let cx = ecx.cx();
+        let maybe_count = types
+            .iter()
+            .filter_map(|ty| ty::EffectKind::try_from_ty(cx, ty))
+            .filter(|&ty| ty == ty::EffectKind::Maybe)
+            .count();
+
+        // Don't do concrete type check unless there are more than one type that will influence the result.
+        // This would allow `(Maybe, T): Min` pass even if we know nothing about `T`.
+        if types.len() - maybe_count > 1 {
+            let mut min = ty::EffectKind::Maybe;
+
+            for ty in types.iter() {
+                let Some(kind) = ty::EffectKind::try_from_ty(ecx.cx(), ty) else {
+                    return Err(NoSolution);
+                };
+
+                let Some(result) = ty::EffectKind::intersection(min, kind) else {
+                    return Err(NoSolution);
+                };
+
+                min = result;
+            }
+        }
+
+        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
+            .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
+    }
 }
 
 impl<D, I> EvalCtxt<'_, D>
@@ -744,7 +787,7 @@ where
             ));
         } else if let Some(a_principal) = a_data.principal() {
             for new_a_principal in
-                D::elaborate_supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
+                elaborate::supertraits(self.cx(), a_principal.with_self_ty(cx, a_ty)).skip(1)
             {
                 responses.extend(self.consider_builtin_upcast_to_principal(
                     goal,
@@ -819,8 +862,7 @@ where
             .auto_traits()
             .into_iter()
             .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
-                self.cx()
-                    .supertrait_def_ids(principal_def_id)
+                elaborate::supertrait_def_ids(self.cx(), principal_def_id)
                     .into_iter()
                     .filter(|def_id| self.cx().trait_is_auto(*def_id))
             }))
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index f08efe60d96..e4c75ac1145 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -45,10 +45,6 @@ parse_bad_assoc_type_bounds = bounds on associated types do not belong here
 parse_bad_item_kind = {$descr} is not supported in {$ctx}
     .help = consider moving the {$descr} out to a nearby module scope
 
-parse_bad_return_type_notation_dotdot =
-    return type notation uses `()` instead of `(..)` for elided arguments
-    .suggestion = remove the `..`
-
 parse_bad_return_type_notation_output =
     return type not allowed with return type notation
     .suggestion = remove the return type
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 8d49887f164..6894f470d88 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -2568,14 +2568,6 @@ pub(crate) struct BadReturnTypeNotationOutput {
 }
 
 #[derive(Diagnostic)]
-#[diag(parse_bad_return_type_notation_dotdot)]
-pub(crate) struct BadReturnTypeNotationDotDot {
-    #[primary_span]
-    #[suggestion(code = "", applicability = "maybe-incorrect")]
-    pub span: Span,
-}
-
-#[derive(Diagnostic)]
 #[diag(parse_bad_assoc_type_bounds)]
 pub(crate) struct BadAssocTypeBounds {
     #[primary_span]
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 58fef9b6c45..a8fe35f45b3 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -282,7 +282,7 @@ impl<'a> Parser<'a> {
     pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
         let mut attrs = ast::AttrVec::new();
         loop {
-            let start_pos: u32 = self.num_bump_calls.try_into().unwrap();
+            let start_pos = self.num_bump_calls;
             // Only try to parse if it is an inner attribute (has `!`).
             let attr = if self.check(&token::Pound) && self.look_ahead(1, |t| t == &token::Not) {
                 Some(self.parse_attribute(InnerAttrPolicy::Permitted)?)
@@ -303,7 +303,7 @@ impl<'a> Parser<'a> {
                 None
             };
             if let Some(attr) = attr {
-                let end_pos: u32 = self.num_bump_calls.try_into().unwrap();
+                let end_pos = self.num_bump_calls;
                 // If we are currently capturing tokens, mark the location of this inner attribute.
                 // If capturing ends up creating a `LazyAttrTokenStream`, we will include
                 // this replace range with it, removing the inner attribute from the final
@@ -313,7 +313,7 @@ impl<'a> Parser<'a> {
                 // corresponding macro).
                 let range = start_pos..end_pos;
                 if let Capturing::Yes = self.capture_state.capturing {
-                    self.capture_state.inner_attr_ranges.insert(attr.id, (range, vec![]));
+                    self.capture_state.inner_attr_ranges.insert(attr.id, (range, None));
                 }
                 attrs.push(attr);
             } else {
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index b5480b6b7d2..38f18022e3c 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -1,6 +1,6 @@
 use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken};
 use rustc_ast::token::{self, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttributesData, DelimSpacing};
+use rustc_ast::tokenstream::{AttrTokenStream, AttrTokenTree, AttrsTarget, DelimSpacing};
 use rustc_ast::tokenstream::{DelimSpan, LazyAttrTokenStream, Spacing, ToAttrTokenStream};
 use rustc_ast::{self as ast};
 use rustc_ast::{AttrVec, Attribute, HasAttrs, HasTokens};
@@ -8,7 +8,7 @@ use rustc_errors::PResult;
 use rustc_session::parse::ParseSess;
 use rustc_span::{sym, Span, DUMMY_SP};
 
-use std::ops::Range;
+use std::{iter, mem};
 
 /// A wrapper type to ensure that the parser handles outer attributes correctly.
 /// When we parse outer attributes, we need to ensure that we capture tokens
@@ -29,15 +29,15 @@ pub struct AttrWrapper {
     // The start of the outer attributes in the token cursor.
     // This allows us to create a `ReplaceRange` for the entire attribute
     // target, including outer attributes.
-    start_pos: usize,
+    start_pos: u32,
 }
 
 impl AttrWrapper {
-    pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper {
+    pub(super) fn new(attrs: AttrVec, start_pos: u32) -> AttrWrapper {
         AttrWrapper { attrs, start_pos }
     }
     pub fn empty() -> AttrWrapper {
-        AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX }
+        AttrWrapper { attrs: AttrVec::new(), start_pos: u32::MAX }
     }
 
     pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
@@ -53,7 +53,7 @@ impl AttrWrapper {
     // FIXME: require passing an NT to prevent misuse of this method
     pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
         let mut self_attrs = self.attrs;
-        std::mem::swap(attrs, &mut self_attrs);
+        mem::swap(attrs, &mut self_attrs);
         attrs.extend(self_attrs);
     }
 
@@ -87,11 +87,10 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
 //
 // This also makes `Parser` very cheap to clone, since
 // there is no intermediate collection buffer to clone.
-#[derive(Clone)]
 struct LazyAttrTokenStreamImpl {
     start_token: (Token, Spacing),
     cursor_snapshot: TokenCursor,
-    num_calls: usize,
+    num_calls: u32,
     break_last_token: bool,
     replace_ranges: Box<[ReplaceRange]>,
 }
@@ -104,15 +103,16 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
         // produce an empty `TokenStream` if no calls were made, and omit the
         // final token otherwise.
         let mut cursor_snapshot = self.cursor_snapshot.clone();
-        let tokens =
-            std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
-                .chain(std::iter::repeat_with(|| {
-                    let token = cursor_snapshot.next();
-                    (FlatToken::Token(token.0), token.1)
-                }))
-                .take(self.num_calls);
-
-        if !self.replace_ranges.is_empty() {
+        let tokens = iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
+            .chain(iter::repeat_with(|| {
+                let token = cursor_snapshot.next();
+                (FlatToken::Token(token.0), token.1)
+            }))
+            .take(self.num_calls as usize);
+
+        if self.replace_ranges.is_empty() {
+            make_attr_token_stream(tokens, self.break_last_token)
+        } else {
             let mut tokens: Vec<_> = tokens.collect();
             let mut replace_ranges = self.replace_ranges.to_vec();
             replace_ranges.sort_by_key(|(range, _)| range.start);
@@ -144,29 +144,26 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             // start position, we ensure that any replace range which encloses
             // another replace range will capture the *replaced* tokens for the inner
             // range, not the original tokens.
-            for (range, new_tokens) in replace_ranges.into_iter().rev() {
+            for (range, target) in replace_ranges.into_iter().rev() {
                 assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
-                // Replace ranges are only allowed to decrease the number of tokens.
-                assert!(
-                    range.len() >= new_tokens.len(),
-                    "Range {range:?} has greater len than {new_tokens:?}"
-                );
-
-                // Replace any removed tokens with `FlatToken::Empty`.
-                // This keeps the total length of `tokens` constant throughout the
-                // replacement process, allowing us to use all of the `ReplaceRanges` entries
-                // without adjusting indices.
-                let filler = std::iter::repeat((FlatToken::Empty, Spacing::Alone))
-                    .take(range.len() - new_tokens.len());
 
+                // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus
+                // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the
+                // total length of `tokens` constant throughout the replacement process, allowing
+                // us to use all of the `ReplaceRanges` entries without adjusting indices.
+                let target_len = target.is_some() as usize;
                 tokens.splice(
                     (range.start as usize)..(range.end as usize),
-                    new_tokens.into_iter().chain(filler),
+                    target
+                        .into_iter()
+                        .map(|target| (FlatToken::AttrsTarget(target), Spacing::Alone))
+                        .chain(
+                            iter::repeat((FlatToken::Empty, Spacing::Alone))
+                                .take(range.len() - target_len),
+                        ),
                 );
             }
-            make_token_stream(tokens.into_iter(), self.break_last_token)
-        } else {
-            make_token_stream(tokens, self.break_last_token)
+            make_attr_token_stream(tokens.into_iter(), self.break_last_token)
         }
     }
 }
@@ -218,24 +215,23 @@ impl<'a> Parser<'a> {
         let start_token = (self.token.clone(), self.token_spacing);
         let cursor_snapshot = self.token_cursor.clone();
         let start_pos = self.num_bump_calls;
-
         let has_outer_attrs = !attrs.attrs.is_empty();
-        let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
         let replace_ranges_start = self.capture_state.replace_ranges.len();
 
-        let ret = f(self, attrs.attrs);
-
-        self.capture_state.capturing = prev_capturing;
-
-        let (mut ret, trailing) = ret?;
+        let (mut ret, trailing) = {
+            let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
+            let ret_and_trailing = f(self, attrs.attrs);
+            self.capture_state.capturing = prev_capturing;
+            ret_and_trailing?
+        };
 
         // When we're not in `capture-cfg` mode, then bail out early if:
         // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`)
         //    so there's nothing for us to do.
         // 2. Our target already has tokens set (e.g. we've parsed something
-        // like `#[my_attr] $item`. The actual parsing code takes care of prepending
-        // any attributes to the nonterminal, so we don't need to modify the
-        // already captured tokens.
+        //    like `#[my_attr] $item`). The actual parsing code takes care of
+        //    prepending any attributes to the nonterminal, so we don't need to
+        //    modify the already captured tokens.
         // Note that this check is independent of `force_collect`- if we already
         // have tokens, or can't even store them, then there's never a need to
         // force collection of new tokens.
@@ -276,37 +272,32 @@ impl<'a> Parser<'a> {
 
         let replace_ranges_end = self.capture_state.replace_ranges.len();
 
-        let mut end_pos = self.num_bump_calls;
-
-        let mut captured_trailing = false;
-
         // Capture a trailing token if requested by the callback 'f'
-        match trailing {
-            TrailingToken::None => {}
+        let captured_trailing = match trailing {
+            TrailingToken::None => false,
             TrailingToken::Gt => {
                 assert_eq!(self.token.kind, token::Gt);
+                false
             }
             TrailingToken::Semi => {
                 assert_eq!(self.token.kind, token::Semi);
-                end_pos += 1;
-                captured_trailing = true;
+                true
             }
-            TrailingToken::MaybeComma => {
-                if self.token.kind == token::Comma {
-                    end_pos += 1;
-                    captured_trailing = true;
-                }
-            }
-        }
+            TrailingToken::MaybeComma => self.token.kind == token::Comma,
+        };
 
-        // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens),
-        // then extend the range of captured tokens to include it, since the parser
-        // was not actually bumped past it. When the `LazyAttrTokenStream` gets converted
-        // into an `AttrTokenStream`, we will create the proper token.
-        if self.break_last_token {
-            assert!(!captured_trailing, "Cannot set break_last_token and have trailing token");
-            end_pos += 1;
-        }
+        assert!(
+            !(self.break_last_token && captured_trailing),
+            "Cannot set break_last_token and have trailing token"
+        );
+
+        let end_pos = self.num_bump_calls
+            + captured_trailing as u32
+            // If we 'broke' the last token (e.g. breaking a '>>' token to two '>' tokens), then
+            // extend the range of captured tokens to include it, since the parser was not actually
+            // bumped past it. When the `LazyAttrTokenStream` gets converted into an
+            // `AttrTokenStream`, we will create the proper token.
+            + self.break_last_token as u32;
 
         let num_calls = end_pos - start_pos;
 
@@ -318,14 +309,11 @@ impl<'a> Parser<'a> {
             // Grab any replace ranges that occur *inside* the current AST node.
             // We will perform the actual replacement when we convert the `LazyAttrTokenStream`
             // to an `AttrTokenStream`.
-            let start_calls: u32 = start_pos.try_into().unwrap();
             self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
                 .iter()
                 .cloned()
                 .chain(inner_attr_replace_ranges.iter().cloned())
-                .map(|(range, tokens)| {
-                    ((range.start - start_calls)..(range.end - start_calls), tokens)
-                })
+                .map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data))
                 .collect()
         };
 
@@ -340,7 +328,7 @@ impl<'a> Parser<'a> {
         // If we support tokens at all
         if let Some(target_tokens) = ret.tokens_mut() {
             if target_tokens.is_none() {
-                // Store se our newly captured tokens into the AST node
+                // Store our newly captured tokens into the AST node.
                 *target_tokens = Some(tokens.clone());
             }
         }
@@ -355,18 +343,14 @@ impl<'a> Parser<'a> {
             && matches!(self.capture_state.capturing, Capturing::Yes)
             && has_cfg_or_cfg_attr(final_attrs)
         {
-            let attr_data = AttributesData { attrs: final_attrs.iter().cloned().collect(), tokens };
+            assert!(!self.break_last_token, "Should not have unglued last token with cfg attr");
 
-            // Replace the entire AST node that we just parsed, including attributes,
-            // with a `FlatToken::AttrTarget`. If this AST node is inside an item
-            // that has `#[derive]`, then this will allow us to cfg-expand this
-            // AST node.
+            // Replace the entire AST node that we just parsed, including attributes, with
+            // `target`. If this AST node is inside an item that has `#[derive]`, then this will
+            // allow us to cfg-expand this AST node.
             let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
-            let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)];
-
-            assert!(!self.break_last_token, "Should not have unglued last token with cfg attr");
-            let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap());
-            self.capture_state.replace_ranges.push((range, new_tokens));
+            let target = AttrsTarget { attrs: final_attrs.iter().cloned().collect(), tokens };
+            self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
             self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
         }
 
@@ -382,10 +366,10 @@ impl<'a> Parser<'a> {
     }
 }
 
-/// Converts a flattened iterator of tokens (including open and close delimiter tokens)
-/// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair
-/// of open and close delims.
-fn make_token_stream(
+/// Converts a flattened iterator of tokens (including open and close delimiter tokens) into an
+/// `AttrTokenStream`, creating an `AttrTokenTree::Delimited` for each matching pair of open and
+/// close delims.
+fn make_attr_token_stream(
     mut iter: impl Iterator<Item = (FlatToken, Spacing)>,
     break_last_token: bool,
 ) -> AttrTokenStream {
@@ -428,11 +412,11 @@ fn make_token_stream(
                 .expect("Bottom token frame is missing!")
                 .inner
                 .push(AttrTokenTree::Token(token, spacing)),
-            FlatToken::AttrTarget(data) => stack
+            FlatToken::AttrsTarget(target) => stack
                 .last_mut()
                 .expect("Bottom token frame is missing!")
                 .inner
-                .push(AttrTokenTree::Attributes(data)),
+                .push(AttrTokenTree::AttrsTarget(target)),
             FlatToken::Empty => {}
         }
         token_and_spacing = iter.next();
@@ -464,6 +448,6 @@ mod size_asserts {
     use rustc_data_structures::static_assert_size;
     // tidy-alphabetical-start
     static_assert_size!(AttrWrapper, 16);
-    static_assert_size!(LazyAttrTokenStreamImpl, 104);
+    static_assert_size!(LazyAttrTokenStreamImpl, 96);
     // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e0c70884fee..b2df9a14eb0 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2327,7 +2327,7 @@ impl<'a> Parser<'a> {
         let before = self.prev_token.clone();
         let binder = if self.check_keyword(kw::For) {
             let lo = self.token.span;
-            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
             let span = lo.to(self.prev_token.span);
 
             self.psess.gated_spans.gate(sym::closure_lifetime_binder, span);
@@ -3432,8 +3432,9 @@ impl<'a> Parser<'a> {
             }
         }
         let capture_clause = self.parse_capture_clause()?;
+        let decl_span = lo.to(self.prev_token.span);
         let (attrs, body) = self.parse_inner_attrs_and_block()?;
-        let kind = ExprKind::Gen(capture_clause, body, kind);
+        let kind = ExprKind::Gen(capture_clause, body, kind, decl_span);
         Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
     }
 
@@ -4022,7 +4023,7 @@ impl MutVisitor for CondChecker<'_> {
             | ExprKind::Match(_, _, _)
             | ExprKind::Closure(_)
             | ExprKind::Block(_, _)
-            | ExprKind::Gen(_, _, _)
+            | ExprKind::Gen(_, _, _, _)
             | ExprKind::TryBlock(_)
             | ExprKind::Underscore
             | ExprKind::Path(_, _)
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index fde16ac957d..10c7715c7dc 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -457,7 +457,7 @@ impl<'a> Parser<'a> {
         // * `for<'a> Trait1<'a>: Trait2<'a /* ok */>`
         // * `(for<'a> Trait1<'a>): Trait2<'a /* not ok */>`
         // * `for<'a> for<'b> Trait1<'a, 'b>: Trait2<'a /* ok */, 'b /* not ok */>`
-        let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+        let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
 
         // Parse type with mandatory colon and (possibly empty) bounds,
         // or with mandatory equality sign and the second type.
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index cfd0a72c056..45ca267fe5d 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -20,7 +20,7 @@ use path::PathStyle;
 
 use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Delimiter, IdentIsRaw, Nonterminal, Token, TokenKind};
-use rustc_ast::tokenstream::{AttributesData, DelimSpacing, DelimSpan, Spacing};
+use rustc_ast::tokenstream::{AttrsTarget, DelimSpacing, DelimSpan, Spacing};
 use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
@@ -153,7 +153,7 @@ pub struct Parser<'a> {
     expected_tokens: Vec<TokenType>,
     token_cursor: TokenCursor,
     // The number of calls to `bump`, i.e. the position in the token stream.
-    num_bump_calls: usize,
+    num_bump_calls: u32,
     // During parsing we may sometimes need to 'unglue' a glued token into two
     // component tokens (e.g. '>>' into '>' and '>), so the parser can consume
     // them one at a time. This process bypasses the normal capturing mechanism
@@ -192,7 +192,7 @@ pub struct Parser<'a> {
 // This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. Make sure
 // it doesn't unintentionally get bigger.
 #[cfg(target_pointer_width = "64")]
-rustc_data_structures::static_assert_size!(Parser<'_>, 264);
+rustc_data_structures::static_assert_size!(Parser<'_>, 256);
 
 /// Stores span information about a closure.
 #[derive(Clone, Debug)]
@@ -203,13 +203,13 @@ struct ClosureSpans {
 }
 
 /// Indicates a range of tokens that should be replaced by
-/// the tokens in the provided vector. This is used in two
+/// the tokens in the provided `AttrsTarget`. This is used in two
 /// places during token collection:
 ///
 /// 1. During the parsing of an AST node that may have a `#[derive]`
 /// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]`
 /// In this case, we use a `ReplaceRange` to replace the entire inner AST node
-/// with `FlatToken::AttrTarget`, allowing us to perform eager cfg-expansion
+/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion
 /// on an `AttrTokenStream`.
 ///
 /// 2. When we parse an inner attribute while collecting tokens. We
@@ -219,7 +219,7 @@ struct ClosureSpans {
 /// the first macro inner attribute to invoke a proc-macro).
 /// When create a `TokenStream`, the inner attributes get inserted
 /// into the proper place in the token stream.
-type ReplaceRange = (Range<u32>, Vec<(FlatToken, Spacing)>);
+type ReplaceRange = (Range<u32>, Option<AttrsTarget>);
 
 /// Controls how we capture tokens. Capturing can be expensive,
 /// so we try to avoid performing capturing in cases where
@@ -1572,7 +1572,7 @@ impl<'a> Parser<'a> {
         self.expected_tokens.clear();
     }
 
-    pub fn approx_token_stream_pos(&self) -> usize {
+    pub fn approx_token_stream_pos(&self) -> u32 {
         self.num_bump_calls
     }
 }
@@ -1608,11 +1608,10 @@ enum FlatToken {
     /// A token - this holds both delimiter (e.g. '{' and '}')
     /// and non-delimiter tokens
     Token(Token),
-    /// Holds the `AttributesData` for an AST node. The
-    /// `AttributesData` is inserted directly into the
-    /// constructed `AttrTokenStream` as
-    /// an `AttrTokenTree::Attributes`.
-    AttrTarget(AttributesData),
+    /// Holds the `AttrsTarget` for an AST node. The `AttrsTarget` is inserted
+    /// directly into the constructed `AttrTokenStream` as an
+    /// `AttrTokenTree::AttrsTarget`.
+    AttrsTarget(AttrsTarget),
     /// A special 'empty' token that is ignored during the conversion
     /// to an `AttrTokenStream`. This is used to simplify the
     /// handling of replace ranges.
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index da8d1194325..03c647dd527 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -353,18 +353,17 @@ impl<'a> Parser<'a> {
                     })?;
                     let span = lo.to(self.prev_token.span);
                     AngleBracketedArgs { args, span }.into()
-                } else if self.may_recover()
-                    && self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
+                } else if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
                     // FIXME(return_type_notation): Could also recover `...` here.
                     && self.look_ahead(1, |tok| tok.kind == token::DotDot)
                 {
-                    self.bump();
-                    self.dcx()
-                        .emit_err(errors::BadReturnTypeNotationDotDot { span: self.token.span });
-                    self.bump();
+                    self.bump(); // (
+                    self.bump(); // ..
                     self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
                     let span = lo.to(self.prev_token.span);
 
+                    self.psess.gated_spans.gate(sym::return_type_notation, span);
+
                     if self.eat_noexpect(&token::RArrow) {
                         let lo = self.prev_token.span;
                         let ty = self.parse_ty()?;
@@ -372,13 +371,7 @@ impl<'a> Parser<'a> {
                             .emit_err(errors::BadReturnTypeNotationOutput { span: lo.to(ty.span) });
                     }
 
-                    ParenthesizedArgs {
-                        span,
-                        inputs: ThinVec::new(),
-                        inputs_span: span,
-                        output: ast::FnRetTy::Default(self.prev_token.span.shrink_to_hi()),
-                    }
-                    .into()
+                    P(ast::GenericArgs::ParenthesizedElided(span))
                 } else {
                     // `(T, U) -> R`
 
@@ -733,14 +726,6 @@ impl<'a> Parser<'a> {
 
                     let span = lo.to(self.prev_token.span);
 
-                    if let AssocItemConstraintKind::Bound { .. } = kind
-                        && let Some(ast::GenericArgs::Parenthesized(args)) = &gen_args
-                        && args.inputs.is_empty()
-                        && let ast::FnRetTy::Default(..) = args.output
-                    {
-                        self.psess.gated_spans.gate(sym::return_type_notation, span);
-                    }
-
                     let constraint =
                         AssocItemConstraint { id: ast::DUMMY_NODE_ID, ident, gen_args, kind, span };
                     Ok(Some(AngleBracketedArg::Constraint(constraint)))
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index d2043c353fe..1e5b227aaa9 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -18,7 +18,7 @@ use rustc_ast::{
 };
 use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::{Span, Symbol};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 use thin_vec::{thin_vec, ThinVec};
 
 #[derive(Copy, Clone, PartialEq)]
@@ -280,7 +280,7 @@ impl<'a> Parser<'a> {
             // Function pointer type or bound list (trait object type) starting with a poly-trait.
             //   `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
             //   `for<'lt> Trait1<'lt> + Trait2 + 'a`
-            let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+            let (lifetime_defs, _) = self.parse_late_bound_lifetime_defs()?;
             if self.check_fn_front_matter(false, Case::Sensitive) {
                 self.parse_ty_bare_fn(
                     lo,
@@ -833,12 +833,9 @@ impl<'a> Parser<'a> {
         let lo = self.token.span;
         let leading_token = self.prev_token.clone();
         let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
-        let inner_lo = self.token.span;
 
-        let modifiers = self.parse_trait_bound_modifiers()?;
         let bound = if self.token.is_lifetime() {
-            self.error_lt_bound_with_modifiers(modifiers);
-            self.parse_generic_lt_bound(lo, inner_lo, has_parens)?
+            self.parse_generic_lt_bound(lo, has_parens)?
         } else if self.eat_keyword(kw::Use) {
             // parse precise captures, if any. This is `use<'lt, 'lt, P, P>`; a list of
             // lifetimes and ident params (including SelfUpper). These are validated later
@@ -848,7 +845,7 @@ impl<'a> Parser<'a> {
             let (args, args_span) = self.parse_precise_capturing_args()?;
             GenericBound::Use(args, use_span.to(args_span))
         } else {
-            self.parse_generic_ty_bound(lo, has_parens, modifiers, &leading_token)?
+            self.parse_generic_ty_bound(lo, has_parens, &leading_token)?
         };
 
         Ok(bound)
@@ -858,50 +855,64 @@ impl<'a> Parser<'a> {
     /// ```ebnf
     /// LT_BOUND = LIFETIME
     /// ```
-    fn parse_generic_lt_bound(
-        &mut self,
-        lo: Span,
-        inner_lo: Span,
-        has_parens: bool,
-    ) -> PResult<'a, GenericBound> {
-        let bound = GenericBound::Outlives(self.expect_lifetime());
+    fn parse_generic_lt_bound(&mut self, lo: Span, has_parens: bool) -> PResult<'a, GenericBound> {
+        let lt = self.expect_lifetime();
+        let bound = GenericBound::Outlives(lt);
         if has_parens {
             // FIXME(Centril): Consider not erroring here and accepting `('lt)` instead,
             // possibly introducing `GenericBound::Paren(P<GenericBound>)`?
-            self.recover_paren_lifetime(lo, inner_lo)?;
+            self.recover_paren_lifetime(lo, lt.ident.span)?;
         }
         Ok(bound)
     }
 
     /// Emits an error if any trait bound modifiers were present.
-    fn error_lt_bound_with_modifiers(&self, modifiers: TraitBoundModifiers) {
-        match modifiers.constness {
+    fn error_lt_bound_with_modifiers(
+        &self,
+        modifiers: TraitBoundModifiers,
+        binder_span: Option<Span>,
+    ) -> ErrorGuaranteed {
+        let TraitBoundModifiers { constness, asyncness, polarity } = modifiers;
+
+        match constness {
             BoundConstness::Never => {}
             BoundConstness::Always(span) | BoundConstness::Maybe(span) => {
-                self.dcx().emit_err(errors::ModifierLifetime {
-                    span,
-                    modifier: modifiers.constness.as_str(),
-                });
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: constness.as_str() });
             }
         }
 
-        match modifiers.polarity {
+        match polarity {
             BoundPolarity::Positive => {}
             BoundPolarity::Negative(span) | BoundPolarity::Maybe(span) => {
-                self.dcx().emit_err(errors::ModifierLifetime {
-                    span,
-                    modifier: modifiers.polarity.as_str(),
-                });
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: polarity.as_str() });
+            }
+        }
+
+        match asyncness {
+            BoundAsyncness::Normal => {}
+            BoundAsyncness::Async(span) => {
+                return self
+                    .dcx()
+                    .emit_err(errors::ModifierLifetime { span, modifier: asyncness.as_str() });
             }
         }
+
+        if let Some(span) = binder_span {
+            return self.dcx().emit_err(errors::ModifierLifetime { span, modifier: "for<...>" });
+        }
+
+        unreachable!("lifetime bound intercepted in `parse_generic_ty_bound` but no modifiers?")
     }
 
     /// Recover on `('lifetime)` with `(` already eaten.
-    fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
-        let inner_span = inner_lo.to(self.prev_token.span);
+    fn recover_paren_lifetime(&mut self, lo: Span, lt_span: Span) -> PResult<'a, ()> {
         self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
         let span = lo.to(self.prev_token.span);
-        let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(inner_span) {
+        let (sugg, snippet) = if let Ok(snippet) = self.span_to_snippet(lt_span) {
             (Some(span), snippet)
         } else {
             (None, String::new())
@@ -916,7 +927,7 @@ impl<'a> Parser<'a> {
     /// If no modifiers are present, this does not consume any tokens.
     ///
     /// ```ebnf
-    /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["?" | "!"]
+    /// TRAIT_BOUND_MODIFIERS = [["~"] "const"] ["async"] ["?" | "!"]
     /// ```
     fn parse_trait_bound_modifiers(&mut self) -> PResult<'a, TraitBoundModifiers> {
         let constness = if self.eat(&token::Tilde) {
@@ -970,15 +981,23 @@ impl<'a> Parser<'a> {
     /// TY_BOUND_NOPAREN = [TRAIT_BOUND_MODIFIERS] [for<LT_PARAM_DEFS>] SIMPLE_PATH
     /// ```
     ///
-    /// For example, this grammar accepts `~const ?for<'a: 'b> m::Trait<'a>`.
+    /// For example, this grammar accepts `for<'a: 'b> ~const ?m::Trait<'a>`.
     fn parse_generic_ty_bound(
         &mut self,
         lo: Span,
         has_parens: bool,
-        modifiers: TraitBoundModifiers,
         leading_token: &Token,
     ) -> PResult<'a, GenericBound> {
-        let mut lifetime_defs = self.parse_late_bound_lifetime_defs()?;
+        let modifiers = self.parse_trait_bound_modifiers()?;
+        let (mut lifetime_defs, binder_span) = self.parse_late_bound_lifetime_defs()?;
+
+        // Recover erroneous lifetime bound with modifiers or binder.
+        // e.g. `T: for<'a> 'a` or `T: ~const 'a`.
+        if self.token.is_lifetime() {
+            let _: ErrorGuaranteed = self.error_lt_bound_with_modifiers(modifiers, binder_span);
+            return self.parse_generic_lt_bound(lo, has_parens);
+        }
+
         let mut path = if self.token.is_keyword(kw::Fn)
             && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
             && let Some(path) = self.recover_path_from_fn()
@@ -1094,16 +1113,19 @@ impl<'a> Parser<'a> {
     }
 
     /// Optionally parses `for<$generic_params>`.
-    pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, ThinVec<GenericParam>> {
+    pub(super) fn parse_late_bound_lifetime_defs(
+        &mut self,
+    ) -> PResult<'a, (ThinVec<GenericParam>, Option<Span>)> {
         if self.eat_keyword(kw::For) {
+            let lo = self.token.span;
             self.expect_lt()?;
             let params = self.parse_generic_params()?;
             self.expect_gt()?;
-            // We rely on AST validation to rule out invalid cases: There must not be type
-            // parameters, and the lifetime parameters must not have bounds.
-            Ok(params)
+            // We rely on AST validation to rule out invalid cases: There must not be
+            // type or const parameters, and parameters must not have bounds.
+            Ok((params, Some(lo.to(self.prev_token.span))))
         } else {
-            Ok(ThinVec::new())
+            Ok((ThinVec::new(), None))
         }
     }
 
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 5a560325ab9..d7513fbad63 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -782,6 +782,7 @@ passes_used_compiler_linker =
 
 passes_used_static =
     attribute must be applied to a `static` variable
+    .label = but this is a {$target}
 
 passes_useless_assignment =
     useless assignment of {$is_field_assign ->
diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs
index 0c3dd649997..839b96fb3de 100644
--- a/compiler/rustc_passes/src/abi_test.rs
+++ b/compiler/rustc_passes/src/abi_test.rs
@@ -61,7 +61,7 @@ fn unwrap_fn_abi<'tcx>(
 fn dump_abi_of_fn_item(tcx: TyCtxt<'_>, item_def_id: LocalDefId, attr: &Attribute) {
     let param_env = tcx.param_env(item_def_id);
     let args = GenericArgs::identity_for_item(tcx, item_def_id);
-    let instance = match Instance::resolve(tcx, param_env, item_def_id.into(), args) {
+    let instance = match Instance::try_resolve(tcx, param_env, item_def_id.into(), args) {
         Ok(Some(instance)) => instance,
         Ok(None) => {
             // Not sure what to do here, but `LayoutError::Unknown` seems reasonable?
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 5f8e4a8b7a7..ce2fa83810f 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -34,8 +34,8 @@ use rustc_session::parse::feature_err;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{BytePos, Span, DUMMY_SP};
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::infer::{TyCtxtInferExt, ValuePairs};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::ObligationCtxt;
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
@@ -274,7 +274,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
 
         self.check_repr(attrs, span, target, item, hir_id);
-        self.check_used(attrs, target);
+        self.check_used(attrs, target, span);
     }
 
     fn inline_attr_str_error_with_macro_def(&self, hir_id: HirId, attr: &Attribute, sym: &str) {
@@ -1930,12 +1930,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         }
     }
 
-    fn check_used(&self, attrs: &[Attribute], target: Target) {
+    fn check_used(&self, attrs: &[Attribute], target: Target, target_span: Span) {
         let mut used_linker_span = None;
         let mut used_compiler_span = None;
         for attr in attrs.iter().filter(|attr| attr.has_name(sym::used)) {
             if target != Target::Static {
-                self.dcx().emit_err(errors::UsedStatic { span: attr.span });
+                self.dcx().emit_err(errors::UsedStatic {
+                    attr_span: attr.span,
+                    span: target_span,
+                    target: target.name(),
+                });
             }
             let inner = attr.meta_item_list();
             match inner.as_deref() {
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index bbd586386dd..55514883cb1 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -102,7 +102,7 @@ fn ty_ref_to_pub_struct(tcx: TyCtxt<'_>, ty: &hir::Ty<'_>) -> Publicness {
     Publicness::new(true, true)
 }
 
-/// Determine if a work from the worklist is coming from the a `#[allow]`
+/// Determine if a work from the worklist is coming from a `#[allow]`
 /// or a `#[expect]` of `dead_code`
 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
 enum ComesFromAllowExpect {
@@ -277,7 +277,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         pats: &[hir::PatField<'_>],
     ) {
         let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
-            ty::Adt(adt, _) => adt.variant_of_res(res),
+            ty::Adt(adt, _) => {
+                self.check_def_id(adt.did());
+                adt.variant_of_res(res)
+            }
             _ => span_bug!(lhs.span, "non-ADT in struct pattern"),
         };
         for pat in pats {
@@ -297,7 +300,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
         dotdot: hir::DotDotPos,
     ) {
         let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
-            ty::Adt(adt, _) => adt.variant_of_res(res),
+            ty::Adt(adt, _) => {
+                self.check_def_id(adt.did());
+                adt.variant_of_res(res)
+            }
             _ => {
                 self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
                 return;
@@ -402,31 +408,6 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                 return false;
             }
 
-            // don't ignore impls for Enums and pub Structs whose methods don't have self receiver,
-            // cause external crate may call such methods to construct values of these types
-            if let Some(local_impl_of) = impl_of.as_local()
-                && let Some(local_def_id) = def_id.as_local()
-                && let Some(fn_sig) =
-                    self.tcx.hir().fn_sig_by_hir_id(self.tcx.local_def_id_to_hir_id(local_def_id))
-                && matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None)
-                && let TyKind::Path(hir::QPath::Resolved(_, path)) =
-                    self.tcx.hir().expect_item(local_impl_of).expect_impl().self_ty.kind
-                && let Res::Def(def_kind, did) = path.res
-            {
-                match def_kind {
-                    // for example, #[derive(Default)] pub struct T(i32);
-                    // external crate can call T::default() to construct T,
-                    // so that don't ignore impl Default for pub Enum and Structs
-                    DefKind::Struct | DefKind::Union if self.tcx.visibility(did).is_public() => {
-                        return false;
-                    }
-                    // don't ignore impl Default for Enums,
-                    // cause we don't know which variant is constructed
-                    DefKind::Enum => return false,
-                    _ => (),
-                };
-            }
-
             if let Some(trait_of) = self.tcx.trait_id_of_impl(impl_of)
                 && self.tcx.has_attr(trait_of, sym::rustc_trivial_field_reads)
             {
@@ -690,6 +671,9 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
                 self.handle_field_pattern_match(pat, res, fields);
             }
             PatKind::Path(ref qpath) => {
+                if let ty::Adt(adt, _) = self.typeck_results().node_type(pat.hir_id).kind() {
+                    self.check_def_id(adt.did());
+                }
                 let res = self.typeck_results().qpath_res(qpath, pat.hir_id);
                 self.handle_res(res);
             }
@@ -845,7 +829,7 @@ fn check_item<'tcx>(
                 // mark the method live if the self_ty is public,
                 // or the method is public and may construct self
                 if tcx.visibility(local_def_id).is_public()
-                    && (ty_and_all_fields_are_public || may_construct_self)
+                    && (ty_and_all_fields_are_public || (ty_is_public && may_construct_self))
                 {
                     // if the impl item is public,
                     // and the ty may be constructed or can be constructed in foreign crates,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 7734dba3670..a026ff3b13b 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -551,7 +551,10 @@ pub struct ReprConflictingLint;
 #[diag(passes_used_static)]
 pub struct UsedStatic {
     #[primary_span]
+    pub attr_span: Span,
+    #[label]
     pub span: Span,
+    pub target: &'static str,
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 0ba61f8e8b4..0720efebf97 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -695,7 +695,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_generic_args(&mut self, g: &'v ast::GenericArgs) {
         record_variants!(
             (self, g, g, Id::None, ast, GenericArgs, GenericArgs),
-            [AngleBracketed, Parenthesized]
+            [AngleBracketed, Parenthesized, ParenthesizedElided]
         );
         ast_visit::walk_generic_args(self, g)
     }
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index c9a47650456..603e98cfa92 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -8,7 +8,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_target::abi::{HasDataLayout, TargetDataLayout};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtExt;
 use rustc_trait_selection::{infer::TyCtxtInferExt, traits};
 
 use crate::errors::{
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index da4435ebebe..dee8ba7e87d 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -30,7 +30,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::Node;
 use rustc_middle::bug;
-use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::middle::privacy::{self, Level};
 use rustc_middle::mir::interpret::{ConstAllocation, ErrorHandled, GlobalAlloc};
 use rustc_middle::query::Providers;
@@ -178,7 +178,15 @@ impl<'tcx> ReachableContext<'tcx> {
         if !self.any_library {
             // If we are building an executable, only explicitly extern
             // types need to be exported.
-            if has_custom_linkage(self.tcx, search_item) {
+            let codegen_attrs = if self.tcx.def_kind(search_item).has_codegen_attrs() {
+                self.tcx.codegen_fn_attrs(search_item)
+            } else {
+                CodegenFnAttrs::EMPTY
+            };
+            let is_extern = codegen_attrs.contains_extern_indicator();
+            let std_internal =
+                codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL);
+            if is_extern || std_internal {
                 self.reachable_symbols.insert(search_item);
             }
         } else {
@@ -302,7 +310,7 @@ impl<'tcx> ReachableContext<'tcx> {
                 GlobalAlloc::Static(def_id) => {
                     self.propagate_item(Res::Def(self.tcx.def_kind(def_id), def_id))
                 }
-                GlobalAlloc::Function(instance) => {
+                GlobalAlloc::Function { instance, .. } => {
                     // Manually visit to actually see the instance's `DefId`. Type visitors won't see it
                     self.propagate_item(Res::Def(
                         self.tcx.def_kind(instance.def_id()),
diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml
index b6ae54010c2..b71853b871d 100644
--- a/compiler/rustc_resolve/Cargo.toml
+++ b/compiler/rustc_resolve/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-pulldown-cmark = { version = "0.9.6", default-features = false }
+pulldown-cmark = { version = "0.11", features = ["html"], default-features = false }
 rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 4e0f2792d97..92cf73870ff 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -321,8 +321,14 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
             // The fields are not expanded yet.
             return;
         }
-        let def_ids = fields.iter().map(|field| self.r.local_def_id(field.id).to_def_id());
-        self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids));
+        let fields = fields
+            .iter()
+            .enumerate()
+            .map(|(i, field)| {
+                field.ident.unwrap_or_else(|| Ident::from_str_and_span(&format!("{i}"), field.span))
+            })
+            .collect();
+        self.r.field_names.insert(def_id, fields);
     }
 
     fn insert_field_visibilities_local(&mut self, def_id: DefId, fields: &[ast::FieldDef]) {
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 1bca5602a4e..a4fdb4a0baf 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -8,6 +8,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_span::hygiene::LocalExpnId;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::Span;
+use std::mem;
 use tracing::debug;
 
 pub(crate) fn collect_definitions(
@@ -15,8 +16,9 @@ pub(crate) fn collect_definitions(
     fragment: &AstFragment,
     expansion: LocalExpnId,
 ) {
-    let (parent_def, impl_trait_context) = resolver.invocation_parents[&expansion];
-    fragment.visit_with(&mut DefCollector { resolver, parent_def, expansion, impl_trait_context });
+    let (parent_def, impl_trait_context, in_attr) = resolver.invocation_parents[&expansion];
+    let mut visitor = DefCollector { resolver, parent_def, expansion, impl_trait_context, in_attr };
+    fragment.visit_with(&mut visitor);
 }
 
 /// Creates `DefId`s for nodes in the AST.
@@ -24,6 +26,7 @@ struct DefCollector<'a, 'b, 'tcx> {
     resolver: &'a mut Resolver<'b, 'tcx>,
     parent_def: LocalDefId,
     impl_trait_context: ImplTraitContext,
+    in_attr: bool,
     expansion: LocalExpnId,
 }
 
@@ -53,7 +56,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
     }
 
     fn with_parent<F: FnOnce(&mut Self)>(&mut self, parent_def: LocalDefId, f: F) {
-        let orig_parent_def = std::mem::replace(&mut self.parent_def, parent_def);
+        let orig_parent_def = mem::replace(&mut self.parent_def, parent_def);
         f(self);
         self.parent_def = orig_parent_def;
     }
@@ -63,7 +66,7 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
         impl_trait_context: ImplTraitContext,
         f: F,
     ) {
-        let orig_itc = std::mem::replace(&mut self.impl_trait_context, impl_trait_context);
+        let orig_itc = mem::replace(&mut self.impl_trait_context, impl_trait_context);
         f(self);
         self.impl_trait_context = orig_itc;
     }
@@ -105,8 +108,10 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
 
     fn visit_macro_invoc(&mut self, id: NodeId) {
         let id = id.placeholder_to_expn_id();
-        let old_parent =
-            self.resolver.invocation_parents.insert(id, (self.parent_def, self.impl_trait_context));
+        let old_parent = self
+            .resolver
+            .invocation_parents
+            .insert(id, (self.parent_def, self.impl_trait_context, self.in_attr));
         assert!(old_parent.is_none(), "parent `LocalDefId` is reset for an invocation");
     }
 }
@@ -334,7 +339,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
                     None => closure_def,
                 }
             }
-            ExprKind::Gen(_, _, _) => {
+            ExprKind::Gen(_, _, _, _) => {
                 self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span)
             }
             ExprKind::ConstBlock(ref constant) => {
@@ -413,4 +418,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
             visit::walk_crate(self, krate)
         }
     }
+
+    fn visit_attribute(&mut self, attr: &'a Attribute) -> Self::Result {
+        let orig_in_attr = mem::replace(&mut self.in_attr, true);
+        visit::walk_attribute(self, attr);
+        self.in_attr = orig_in_attr;
+    }
 }
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 263daa11ec3..ffd495aa985 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1726,11 +1726,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         )) = binding.kind
         {
             let def_id = self.tcx.parent(ctor_def_id);
-            return self
-                .field_def_ids(def_id)?
-                .iter()
-                .map(|&field_id| self.def_span(field_id))
-                .reduce(Span::to); // None for `struct Foo()`
+            return self.field_idents(def_id)?.iter().map(|&f| f.span).reduce(Span::to); // None for `struct Foo()`
         }
         None
     }
@@ -1987,10 +1983,20 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             candidates
                 .sort_by_cached_key(|c| (c.path.segments.len(), pprust::path_to_string(&c.path)));
             if let Some(candidate) = candidates.get(0) {
+                let path = {
+                    // remove the possible common prefix of the path
+                    let start_index = (0..failed_segment_idx)
+                        .find(|&i| path[i].ident != candidate.path.segments[i].ident)
+                        .unwrap_or_default();
+                    let segments = (start_index..=failed_segment_idx)
+                        .map(|s| candidate.path.segments[s].clone())
+                        .collect();
+                    Path { segments, span: Span::default(), tokens: None }
+                };
                 (
                     String::from("unresolved import"),
                     Some((
-                        vec![(ident.span, pprust::path_to_string(&candidate.path))],
+                        vec![(ident.span, pprust::path_to_string(&path))],
                         String::from("a similar path exists"),
                         Applicability::MaybeIncorrect,
                     )),
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 66a1c05289b..1d37264f96a 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -1221,6 +1221,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
                         }
                     }
                 }
+                GenericArgs::ParenthesizedElided(_) => {}
             }
         }
     }
@@ -1743,7 +1744,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                             ) {
                                 self.r.dcx().emit_err(errors::LendingIteratorReportError {
                                     lifetime: lifetime.ident.span,
-                                    ty: ty.span(),
+                                    ty: ty.span,
                                 });
                             } else {
                                 self.r.dcx().emit_err(errors::AnonymousLivetimeNonGatReportError {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 764cc350182..941fb6436df 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1532,17 +1532,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                     if !this.has_private_fields(def_id) {
                         // If the fields of the type are private, we shouldn't be suggesting using
                         // the struct literal syntax at all, as that will cause a subsequent error.
-                        let field_ids = this.r.field_def_ids(def_id);
-                        let (fields, applicability) = match field_ids {
-                            Some(field_ids) => {
-                                let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id));
-
+                        let fields = this.r.field_idents(def_id);
+                        let has_fields = fields.as_ref().is_some_and(|f| !f.is_empty());
+                        let (fields, applicability) = match fields {
+                            Some(fields) => {
                                 let fields = if let Some(old_fields) = old_fields {
                                     fields
+                                        .iter()
                                         .enumerate()
                                         .map(|(idx, new)| (new, old_fields.get(idx)))
                                         .map(|(new, old)| {
-                                            let new = new.to_ident_string();
+                                            let new = new.name.to_ident_string();
                                             if let Some(Some(old)) = old
                                                 && new != *old
                                             {
@@ -1553,17 +1553,17 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                                         })
                                         .collect::<Vec<String>>()
                                 } else {
-                                    fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>()
+                                    fields
+                                        .iter()
+                                        .map(|f| format!("{f}{tail}"))
+                                        .collect::<Vec<String>>()
                                 };
 
                                 (fields.join(", "), applicability)
                             }
                             None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
                         };
-                        let pad = match field_ids {
-                            Some([]) => "",
-                            _ => " ",
-                        };
+                        let pad = if has_fields { " " } else { "" };
                         err.span_suggestion(
                             span,
                             format!("use struct {descr} syntax instead"),
@@ -1723,12 +1723,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                             &args[..],
                         );
                         // Use spans of the tuple struct definition.
-                        self.r.field_def_ids(def_id).map(|field_ids| {
-                            field_ids
-                                .iter()
-                                .map(|&field_id| self.r.def_span(field_id))
-                                .collect::<Vec<_>>()
-                        })
+                        self.r
+                            .field_idents(def_id)
+                            .map(|fields| fields.iter().map(|f| f.span).collect::<Vec<_>>())
                     }
                     _ => None,
                 };
@@ -1791,7 +1788,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             (Res::Def(DefKind::Ctor(_, CtorKind::Fn), ctor_def_id), _) if ns == ValueNS => {
                 let def_id = self.r.tcx.parent(ctor_def_id);
                 err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here"));
-                let fields = self.r.field_def_ids(def_id).map_or_else(
+                let fields = self.r.field_idents(def_id).map_or_else(
                     || "/* fields */".to_string(),
                     |field_ids| vec!["_"; field_ids.len()].join(", "),
                 );
@@ -2017,12 +2014,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                     if let Some(Res::Def(DefKind::Struct | DefKind::Union, did)) =
                         resolution.full_res()
                     {
-                        if let Some(field_ids) = self.r.field_def_ids(did) {
-                            if let Some(field_id) = field_ids
-                                .iter()
-                                .find(|&&field_id| ident.name == self.r.tcx.item_name(field_id))
-                            {
-                                return Some(AssocSuggestion::Field(self.r.def_span(*field_id)));
+                        if let Some(fields) = self.r.field_idents(did) {
+                            if let Some(field) = fields.iter().find(|id| ident.name == id.name) {
+                                return Some(AssocSuggestion::Field(field.span));
                             }
                         }
                     }
@@ -2418,7 +2412,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 match kind {
                     CtorKind::Const => false,
                     CtorKind::Fn => {
-                        !self.r.field_def_ids(def_id).is_some_and(|field_ids| field_ids.is_empty())
+                        !self.r.field_idents(def_id).is_some_and(|field_ids| field_ids.is_empty())
                     }
                 }
             };
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 94cdce1025f..7cb5a3fb2fd 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -350,6 +350,7 @@ impl<'a> From<&'a ast::PathSegment> for Segment {
                     (args.span, found_lifetimes)
                 }
                 GenericArgs::Parenthesized(args) => (args.span, true),
+                GenericArgs::ParenthesizedElided(span) => (*span, true),
             }
         } else {
             (DUMMY_SP, false)
@@ -990,7 +991,7 @@ pub struct Resolver<'a, 'tcx> {
     extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'a>>,
 
     /// N.B., this is used only for better diagnostics, not name resolution itself.
-    field_def_ids: LocalDefIdMap<&'tcx [DefId]>,
+    field_names: LocalDefIdMap<Vec<Ident>>,
 
     /// Span of the privacy modifier in fields of an item `DefId` accessible with dot syntax.
     /// Used for hints during error reporting.
@@ -1138,7 +1139,7 @@ pub struct Resolver<'a, 'tcx> {
     /// When collecting definitions from an AST fragment produced by a macro invocation `ExpnId`
     /// we know what parent node that fragment should be attached to thanks to this table,
     /// and how the `impl Trait` fragments were introduced.
-    invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext)>,
+    invocation_parents: FxHashMap<LocalExpnId, (LocalDefId, ImplTraitContext, bool /*in_attr*/)>,
 
     /// Some way to know that we are in a *trait* impl in `visit_assoc_item`.
     /// FIXME: Replace with a more general AST map (together with some other fields).
@@ -1370,7 +1371,8 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         node_id_to_def_id.insert(CRATE_NODE_ID, crate_feed);
 
         let mut invocation_parents = FxHashMap::default();
-        invocation_parents.insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential));
+        invocation_parents
+            .insert(LocalExpnId::ROOT, (CRATE_DEF_ID, ImplTraitContext::Existential, false));
 
         let mut extern_prelude: FxHashMap<Ident, ExternPreludeEntry<'_>> = tcx
             .sess
@@ -1405,7 +1407,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             prelude: None,
             extern_prelude,
 
-            field_def_ids: Default::default(),
+            field_names: Default::default(),
             field_visibility_spans: FxHashMap::default(),
 
             determined_imports: Vec::new(),
@@ -2126,10 +2128,18 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
     }
 
-    fn field_def_ids(&self, def_id: DefId) -> Option<&'tcx [DefId]> {
+    fn field_idents(&self, def_id: DefId) -> Option<Vec<Ident>> {
         match def_id.as_local() {
-            Some(def_id) => self.field_def_ids.get(&def_id).copied(),
-            None => Some(self.tcx.associated_item_def_ids(def_id)),
+            Some(def_id) => self.field_names.get(&def_id).cloned(),
+            None => Some(
+                self.tcx
+                    .associated_item_def_ids(def_id)
+                    .iter()
+                    .map(|&def_id| {
+                        Ident::new(self.tcx.item_name(def_id), self.tcx.def_span(def_id))
+                    })
+                    .collect(),
+            ),
         }
     }
 
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 026a2ca1412..cb9bebd33d3 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -297,11 +297,12 @@ impl<'a, 'tcx> ResolverExpand for Resolver<'a, 'tcx> {
             .invocation_parents
             .get(&invoc_id)
             .or_else(|| self.invocation_parents.get(&eager_expansion_root))
-            .map(|&(mod_def_id, _)| mod_def_id)
-            .filter(|&mod_def_id| {
-                invoc.fragment_kind == AstFragmentKind::Expr
+            .filter(|&&(mod_def_id, _, in_attr)| {
+                in_attr
+                    && invoc.fragment_kind == AstFragmentKind::Expr
                     && self.tcx.def_kind(mod_def_id) == DefKind::Mod
-            });
+            })
+            .map(|&(mod_def_id, ..)| mod_def_id);
         let (ext, res) = self.smart_resolve_macro_path(
             path,
             kind,
diff --git a/compiler/rustc_resolve/src/rustdoc.rs b/compiler/rustc_resolve/src/rustdoc.rs
index 66b4981eb55..59460815321 100644
--- a/compiler/rustc_resolve/src/rustdoc.rs
+++ b/compiler/rustc_resolve/src/rustdoc.rs
@@ -1,4 +1,6 @@
-use pulldown_cmark::{BrokenLink, CowStr, Event, LinkType, Options, Parser, Tag};
+use pulldown_cmark::{
+    BrokenLink, BrokenLinkCallback, CowStr, Event, LinkType, Options, Parser, Tag,
+};
 use rustc_ast as ast;
 use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::FxHashMap;
@@ -427,7 +429,9 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
 
     while let Some(event) = event_iter.next() {
         match event {
-            Event::Start(Tag::Link(link_type, dest, _)) if may_be_doc_link(link_type) => {
+            Event::Start(Tag::Link { link_type, dest_url, title: _, id: _ })
+                if may_be_doc_link(link_type) =>
+            {
                 if matches!(
                     link_type,
                     LinkType::Inline
@@ -441,7 +445,7 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
                     }
                 }
 
-                links.push(preprocess_link(&dest));
+                links.push(preprocess_link(&dest_url));
             }
             _ => {}
         }
@@ -451,8 +455,8 @@ fn parse_links<'md>(doc: &'md str) -> Vec<Box<str>> {
 }
 
 /// Collects additional data of link.
-fn collect_link_data<'input, 'callback>(
-    event_iter: &mut Parser<'input, 'callback>,
+fn collect_link_data<'input, F: BrokenLinkCallback<'input>>(
+    event_iter: &mut Parser<'input, F>,
 ) -> Option<Box<str>> {
     let mut display_text: Option<String> = None;
     let mut append_text = |text: CowStr<'_>| {
diff --git a/compiler/rustc_serialize/src/opaque.rs b/compiler/rustc_serialize/src/opaque.rs
index 1dcb69920d7..d27dfd88824 100644
--- a/compiler/rustc_serialize/src/opaque.rs
+++ b/compiler/rustc_serialize/src/opaque.rs
@@ -155,7 +155,7 @@ impl FileEncoder {
         if std::intrinsics::unlikely(self.buffered > flush_threshold) {
             self.flush();
         }
-        // SAFETY: We checked above that that N < self.buffer_empty().len(),
+        // SAFETY: We checked above that N < self.buffer_empty().len(),
         // and if isn't, flush ensures that our empty buffer is now BUF_SIZE.
         // We produce a post-mono error if N > BUF_SIZE.
         let buf = unsafe { self.buffer_empty().first_chunk_mut::<N>().unwrap_unchecked() };
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 2d38ad37133..41c99f7edee 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2965,8 +2965,9 @@ pub(crate) mod dep_tracking {
         CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
         InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
         LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
-        Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
-        SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
+        PatchableFunctionEntry, Polonius, RemapPathScopeComponents, ResolveDocLinks,
+        SourceFileHashAlgorithm, SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion,
+        WasiExecModel,
     };
     use crate::lint;
     use crate::utils::NativeLib;
@@ -3073,6 +3074,7 @@ pub(crate) mod dep_tracking {
         OomStrategy,
         LanguageIdentifier,
         NextSolverConfig,
+        PatchableFunctionEntry,
         Polonius,
         InliningThreshold,
         FunctionReturn,
@@ -3250,6 +3252,35 @@ impl DumpMonoStatsFormat {
     }
 }
 
+/// `-Z patchable-function-entry` representation - how many nops to put before and after function
+/// entry.
+#[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
+pub struct PatchableFunctionEntry {
+    /// Nops before the entry
+    prefix: u8,
+    /// Nops after the entry
+    entry: u8,
+}
+
+impl PatchableFunctionEntry {
+    pub fn from_total_and_prefix_nops(
+        total_nops: u8,
+        prefix_nops: u8,
+    ) -> Option<PatchableFunctionEntry> {
+        if total_nops < prefix_nops {
+            None
+        } else {
+            Some(Self { prefix: prefix_nops, entry: total_nops - prefix_nops })
+        }
+    }
+    pub fn prefix(&self) -> u8 {
+        self.prefix
+    }
+    pub fn entry(&self) -> u8 {
+        self.entry
+    }
+}
+
 /// `-Zpolonius` values, enabling the borrow checker polonius analysis, and which version: legacy,
 /// or future prototype.
 #[derive(Clone, Copy, PartialEq, Hash, Debug, Default)]
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index 9a10adeb6d1..7421cae65e4 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -379,6 +379,7 @@ mod desc {
     pub const parse_passes: &str = "a space-separated list of passes, or `all`";
     pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
     pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
+    pub const parse_patchable_function_entry: &str = "either two comma separated integers (total_nops,prefix_nops), with prefix_nops <= total_nops, or one integer (total_nops)";
     pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
     pub const parse_oom_strategy: &str = "either `panic` or `abort`";
     pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@@ -734,6 +735,32 @@ mod parse {
         true
     }
 
+    pub(crate) fn parse_patchable_function_entry(
+        slot: &mut PatchableFunctionEntry,
+        v: Option<&str>,
+    ) -> bool {
+        let mut total_nops = 0;
+        let mut prefix_nops = 0;
+
+        if !parse_number(&mut total_nops, v) {
+            let parts = v.and_then(|v| v.split_once(',')).unzip();
+            if !parse_number(&mut total_nops, parts.0) {
+                return false;
+            }
+            if !parse_number(&mut prefix_nops, parts.1) {
+                return false;
+            }
+        }
+
+        if let Some(pfe) =
+            PatchableFunctionEntry::from_total_and_prefix_nops(total_nops, prefix_nops)
+        {
+            *slot = pfe;
+            return true;
+        }
+        false
+    }
+
     pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
         match v {
             Some("panic") => *slot = OomStrategy::Panic,
@@ -1603,8 +1630,6 @@ options! {
         "only allow the listed language features to be enabled in code (comma separated)"),
     always_encode_mir: bool = (false, parse_bool, [TRACKED],
         "encode MIR of all functions into the crate metadata (default: no)"),
-    asm_comments: bool = (false, parse_bool, [TRACKED],
-        "generate comments into the assembly (may change behavior) (default: no)"),
     assert_incr_state: Option<String> = (None, parse_opt_string, [UNTRACKED],
         "assert that the incremental cache is in given state: \
          either `loaded` or `not-loaded`."),
@@ -1739,6 +1764,8 @@ options! {
         "enable LLVM inlining (default: yes)"),
     inline_mir: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "enable MIR inlining (default: no)"),
+    inline_mir_forwarder_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
+        "inlining threshold when the caller is a simple forwarding function (default: 30)"),
     inline_mir_hint_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
         "inlining threshold for functions with inline hint (default: 100)"),
     inline_mir_preserve_debug: Option<bool> = (None, parse_opt_bool, [TRACKED],
@@ -1859,6 +1886,8 @@ options! {
         "panic strategy for panics in drops"),
     parse_only: bool = (false, parse_bool, [UNTRACKED],
         "parse only; do not compile, assemble, or link (default: no)"),
+    patchable_function_entry: PatchableFunctionEntry = (PatchableFunctionEntry::default(), parse_patchable_function_entry, [TRACKED],
+        "nop padding at function entry"),
     plt: Option<bool> = (None, parse_opt_bool, [TRACKED],
         "whether to use the PLT when calling into shared libraries;
         only has effect for PIC code on systems with ELF binaries
@@ -2076,6 +2105,8 @@ written to standard error output)"),
         "Generate sync unwind tables instead of async unwind tables (default: no)"),
     validate_mir: bool = (false, parse_bool, [UNTRACKED],
         "validate MIR after each transformation"),
+    verbose_asm: bool = (false, parse_bool, [TRACKED],
+        "add descriptive comments from LLVM to the assembly (may change behavior) (default: no)"),
     #[rustc_lint_opt_deny_field_access("use `Session::verbose_internals` instead of this field")]
     verbose_internals: bool = (false, parse_bool, [TRACKED_NO_CRATE_HASH],
         "in general, enable more debug printouts (default: no)"),
diff --git a/compiler/rustc_smir/Cargo.toml b/compiler/rustc_smir/Cargo.toml
index 1e0a60bc371..1230667ee91 100644
--- a/compiler/rustc_smir/Cargo.toml
+++ b/compiler/rustc_smir/Cargo.toml
@@ -6,6 +6,8 @@ edition = "2021"
 [dependencies]
 # tidy-alphabetical-start
 rustc_abi = { path = "../rustc_abi" }
+rustc_ast = { path = "../rustc_ast" }
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_middle = { path = "../rustc_middle" }
diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs
index dde5e30c3d0..b0ced8e920f 100644
--- a/compiler/rustc_smir/src/rustc_smir/context.rs
+++ b/compiler/rustc_smir/src/rustc_smir/context.rs
@@ -160,7 +160,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     fn predicates_of(&self, def_id: stable_mir::DefId) -> stable_mir::ty::GenericPredicates {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.predicates_of(def_id);
+        let GenericPredicates { parent, predicates, effects_min_tys: _ } =
+            tables.tcx.predicates_of(def_id);
         stable_mir::ty::GenericPredicates {
             parent: parent.map(|did| tables.trait_def(did)),
             predicates: predicates
@@ -181,7 +182,8 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
     ) -> stable_mir::ty::GenericPredicates {
         let mut tables = self.0.borrow_mut();
         let def_id = tables[def_id];
-        let GenericPredicates { parent, predicates } = tables.tcx.explicit_predicates_of(def_id);
+        let GenericPredicates { parent, predicates, effects_min_tys: _ } =
+            tables.tcx.explicit_predicates_of(def_id);
         stable_mir::ty::GenericPredicates {
             parent: parent.map(|did| tables.trait_def(did)),
             predicates: predicates
@@ -228,6 +230,46 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         }
     }
 
+    fn get_attrs_by_path(
+        &self,
+        def_id: stable_mir::DefId,
+        attr: &[stable_mir::Symbol],
+    ) -> Vec<stable_mir::crate_def::Attribute> {
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let did = tables[def_id];
+        let attr_name: Vec<_> =
+            attr.iter().map(|seg| rustc_span::symbol::Symbol::intern(&seg)).collect();
+        tcx.get_attrs_by_path(did, &attr_name)
+            .map(|attribute| {
+                let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
+                let span = attribute.span;
+                stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
+            })
+            .collect()
+    }
+
+    fn get_all_attrs(&self, def_id: stable_mir::DefId) -> Vec<stable_mir::crate_def::Attribute> {
+        let mut tables = self.0.borrow_mut();
+        let tcx = tables.tcx;
+        let did = tables[def_id];
+        let filter_fn = move |a: &&rustc_ast::ast::Attribute| {
+            matches!(a.kind, rustc_ast::ast::AttrKind::Normal(_))
+        };
+        let attrs_iter = if let Some(did) = did.as_local() {
+            tcx.hir().attrs(tcx.local_def_id_to_hir_id(did)).iter().filter(filter_fn)
+        } else {
+            tcx.item_attrs(did).iter().filter(filter_fn)
+        };
+        attrs_iter
+            .map(|attribute| {
+                let attr_str = rustc_ast_pretty::pprust::attribute_to_string(attribute);
+                let span = attribute.span;
+                stable_mir::crate_def::Attribute::new(attr_str, span.stable(&mut *tables))
+            })
+            .collect()
+    }
+
     fn span_to_string(&self, span: stable_mir::ty::Span) -> String {
         let tables = self.0.borrow();
         tables.tcx.sess.source_map().span_to_diagnostic_string(tables[span])
@@ -587,7 +629,7 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
         let tcx = tables.tcx;
         let def_id = def.0.internal(&mut *tables, tcx);
         let args_ref = args.internal(&mut *tables, tcx);
-        match Instance::resolve(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) {
+        match Instance::try_resolve(tables.tcx, ParamEnv::reveal_all(), def_id, args_ref) {
             Ok(Some(instance)) => Some(instance.stable(&mut *tables)),
             Ok(None) | Err(_) => None,
         }
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index f15b82d0c03..8bfaa112c2f 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -644,6 +644,7 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> {
                 target: target.map(|t| t.as_usize()),
                 unwind: unwind.stable(tables),
             },
+            mir::TerminatorKind::TailCall { func: _, args: _, fn_span: _ } => todo!(),
             mir::TerminatorKind::Assert { cond, expected, msg, target, unwind } => {
                 TerminatorKind::Assert {
                     cond: cond.stable(tables),
@@ -709,7 +710,7 @@ impl<'tcx> Stable<'tcx> for mir::interpret::GlobalAlloc<'tcx> {
 
     fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
         match self {
-            mir::interpret::GlobalAlloc::Function(instance) => {
+            mir::interpret::GlobalAlloc::Function { instance, .. } => {
                 GlobalAlloc::Function(instance.stable(tables))
             }
             mir::interpret::GlobalAlloc::VTable(ty, trait_ref) => {
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 74c3fcc9b9e..9382460d6d4 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -600,7 +600,7 @@ impl<'tcx> Stable<'tcx> for rustc_middle::ty::GenericParamDefKind {
             ty::GenericParamDefKind::Type { has_default, synthetic } => {
                 GenericParamDefKind::Type { has_default: *has_default, synthetic: *synthetic }
             }
-            ty::GenericParamDefKind::Const { has_default, is_host_effect: _ } => {
+            ty::GenericParamDefKind::Const { has_default, is_host_effect: _, synthetic: _ } => {
                 GenericParamDefKind::Const { has_default: *has_default }
             }
         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 6d4a8c29bc9..af56f4e5141 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -194,6 +194,14 @@ symbols! {
         Display,
         DoubleEndedIterator,
         Duration,
+        EffectsCompat,
+        EffectsIntersection,
+        EffectsIntersectionOutput,
+        EffectsMaybe,
+        EffectsNoRuntime,
+        EffectsRuntime,
+        EffectsTyCompat,
+        Effects__,
         Encodable,
         Encoder,
         Enumerate,
@@ -768,6 +776,7 @@ symbols! {
         enable,
         encode,
         end,
+        entry_nops,
         enumerate_method,
         env,
         env_CFG_RELEASE: env!("CFG_RELEASE"),
@@ -1383,6 +1392,7 @@ symbols! {
         passes,
         pat,
         pat_param,
+        patchable_function_entry,
         path,
         pattern_complexity,
         pattern_parentheses,
@@ -1421,6 +1431,7 @@ symbols! {
         prefetch_read_instruction,
         prefetch_write_data,
         prefetch_write_instruction,
+        prefix_nops,
         preg,
         prelude,
         prelude_import,
@@ -1502,6 +1513,7 @@ symbols! {
         recursion_limit,
         reexport_test_harness_main,
         ref_pat_eat_one_layer_2024,
+        ref_pat_eat_one_layer_2024_structural,
         ref_pat_everywhere,
         ref_unwind_safe_trait,
         reference,
@@ -1603,6 +1615,7 @@ symbols! {
         rustc_do_not_const_check,
         rustc_doc_primitive,
         rustc_dummy,
+        rustc_dump_def_parents,
         rustc_dump_item_bounds,
         rustc_dump_predicates,
         rustc_dump_user_args,
@@ -1655,6 +1668,7 @@ symbols! {
         rustc_reallocator,
         rustc_regions,
         rustc_reservation_impl,
+        rustc_runtime,
         rustc_safe_intrinsic,
         rustc_serialize,
         rustc_skip_during_method_dispatch,
diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs
index 9edd2ff9b1a..5aa46cc0dea 100644
--- a/compiler/rustc_symbol_mangling/src/legacy.rs
+++ b/compiler/rustc_symbol_mangling/src/legacy.rs
@@ -85,9 +85,13 @@ pub(super) fn mangle<'tcx>(
         }
         // FIXME(async_closures): This shouldn't be needed when we fix
         // `Instance::ty`/`Instance::def_id`.
-        ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
-        | ty::InstanceKind::CoroutineKindShim { .. } => {
-            printer.write_str("{{fn-once-shim}}").unwrap();
+        ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref, .. } => {
+            printer
+                .write_str(if receiver_by_ref { "{{by-move-shim}}" } else { "{{by-ref-shim}}" })
+                .unwrap();
+        }
+        ty::InstanceKind::CoroutineKindShim { .. } => {
+            printer.write_str("{{by-move-body-shim}}").unwrap();
         }
         _ => {}
     }
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 42c4fa83d1b..5f8029af020 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -49,8 +49,15 @@ pub(super) fn mangle<'tcx>(
         ty::InstanceKind::ReifyShim(_, Some(ReifyReason::FnPtr)) => Some("reify_fnptr"),
         ty::InstanceKind::ReifyShim(_, Some(ReifyReason::Vtable)) => Some("reify_vtable"),
 
-        ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
-        | ty::InstanceKind::CoroutineKindShim { .. } => Some("fn_once"),
+        // FIXME(async_closures): This shouldn't be needed when we fix
+        // `Instance::ty`/`Instance::def_id`.
+        ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: true, .. } => {
+            Some("by_move")
+        }
+        ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => {
+            Some("by_ref")
+        }
+        ty::InstanceKind::CoroutineKindShim { .. } => Some("by_move_body"),
 
         _ => None,
     };
diff --git a/compiler/rustc_target/src/abi/call/mod.rs b/compiler/rustc_target/src/abi/call/mod.rs
index 5713542c17d..8058130f441 100644
--- a/compiler/rustc_target/src/abi/call/mod.rs
+++ b/compiler/rustc_target/src/abi/call/mod.rs
@@ -339,7 +339,9 @@ impl CastTarget {
         }
     }
 
-    pub fn size<C: HasDataLayout>(&self, _cx: &C) -> Size {
+    /// When you only access the range containing valid data, you can use this unaligned size;
+    /// otherwise, use the safer `size` method.
+    pub fn unaligned_size<C: HasDataLayout>(&self, _cx: &C) -> Size {
         // Prefix arguments are passed in specific designated registers
         let prefix_size = self
             .prefix
@@ -353,6 +355,10 @@ impl CastTarget {
         prefix_size + rest_size
     }
 
+    pub fn size<C: HasDataLayout>(&self, cx: &C) -> Size {
+        self.unaligned_size(cx).align_to(self.align(cx))
+    }
+
     pub fn align<C: HasDataLayout>(&self, cx: &C) -> Align {
         self.prefix
             .iter()
diff --git a/compiler/rustc_trait_selection/Cargo.toml b/compiler/rustc_trait_selection/Cargo.toml
index 1f4fb57d996..f023a0eb53a 100644
--- a/compiler/rustc_trait_selection/Cargo.toml
+++ b/compiler/rustc_trait_selection/Cargo.toml
@@ -5,8 +5,6 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-bitflags = "2.4.1"
-derivative = "2.2.0"
 itertools = "0.12"
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_ir = { path = "../rustc_ast_ir" }
@@ -15,7 +13,6 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_fluent_macro = { path = "../rustc_fluent_macro" }
 rustc_hir = { path = "../rustc_hir" }
-rustc_index = { path = "../rustc_index" }
 rustc_infer = { path = "../rustc_infer" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
@@ -28,7 +25,6 @@ rustc_span = { path = "../rustc_span" }
 rustc_target = { path = "../rustc_target" }
 rustc_transmute = { path = "../rustc_transmute", features = ["rustc"] }
 rustc_type_ir = { path = "../rustc_type_ir" }
-rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
 smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_trait_selection/src/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
new file mode 100644
index 00000000000..f6ac8fc7b61
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/mod.rs
@@ -0,0 +1 @@
+pub mod traits;
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
new file mode 100644
index 00000000000..c301deac616
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs
@@ -0,0 +1,698 @@
+use std::ops::ControlFlow;
+
+use rustc_errors::{
+    struct_span_code_err, Applicability, Diag, MultiSpan, StashKey, E0283, E0284, E0790,
+};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor as _;
+use rustc_hir::LangItem;
+use rustc_infer::infer::error_reporting::{TypeAnnotationNeeded, TypeErrCtxt};
+use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
+use rustc_infer::traits::util::elaborate;
+use rustc_infer::traits::{
+    Obligation, ObligationCause, ObligationCauseCode, PolyTraitObligation, PredicateObligation,
+};
+use rustc_macros::extension;
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable as _, TypeVisitableExt as _};
+use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
+
+use crate::error_reporting::traits::suggestions::TypeErrCtxtExt as _;
+use crate::error_reporting::traits::{
+    to_pretty_impl_header, FindExprBySpan, InferCtxtPrivExt as _,
+};
+use crate::traits::query::evaluate_obligation::InferCtxtExt;
+use crate::traits::ObligationCtxt;
+
+#[derive(Debug)]
+pub enum CandidateSource {
+    DefId(DefId),
+    ParamEnv(Span),
+}
+
+pub fn compute_applicable_impls_for_diagnostics<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    obligation: &PolyTraitObligation<'tcx>,
+) -> Vec<CandidateSource> {
+    let tcx = infcx.tcx;
+    let param_env = obligation.param_env;
+
+    let predicate_polarity = obligation.predicate.skip_binder().polarity;
+
+    let impl_may_apply = |impl_def_id| {
+        let ocx = ObligationCtxt::new(infcx);
+        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
+            let obligation_trait_ref = ocx.normalize(
+                &ObligationCause::dummy(),
+                param_env,
+                placeholder_obligation.trait_ref,
+            );
+
+            let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
+            let impl_trait_ref =
+                tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
+            let impl_trait_ref =
+                ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
+
+            if let Err(_) =
+                ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
+            {
+                return false;
+            }
+
+            let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
+            let impl_polarity = impl_trait_header.polarity;
+
+            match (impl_polarity, predicate_polarity) {
+                (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
+                | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {}
+                _ => return false,
+            }
+
+            let obligations = tcx
+                .predicates_of(impl_def_id)
+                .instantiate(tcx, impl_args)
+                .into_iter()
+                .map(|(predicate, _)| {
+                    Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
+                })
+                // Kinda hacky, but let's just throw away obligations that overflow.
+                // This may reduce the accuracy of this check (if the obligation guides
+                // inference or it actually resulted in error after others are processed)
+                // ... but this is diagnostics code.
+                .filter(|obligation| {
+                    infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok()
+                });
+            ocx.register_obligations(obligations);
+
+            ocx.select_where_possible().is_empty()
+        })
+    };
+
+    let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
+        let ocx = ObligationCtxt::new(infcx);
+        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
+            let obligation_trait_ref = ocx.normalize(
+                &ObligationCause::dummy(),
+                param_env,
+                placeholder_obligation.trait_ref,
+            );
+
+            let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
+                DUMMY_SP,
+                BoundRegionConversionTime::HigherRankedType,
+                poly_trait_predicate,
+            );
+            let param_env_trait_ref =
+                ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
+
+            if let Err(_) = ocx.eq(
+                &ObligationCause::dummy(),
+                param_env,
+                obligation_trait_ref,
+                param_env_trait_ref,
+            ) {
+                return false;
+            }
+
+            ocx.select_where_possible().is_empty()
+        })
+    };
+
+    let mut ambiguities = Vec::new();
+
+    tcx.for_each_relevant_impl(
+        obligation.predicate.def_id(),
+        obligation.predicate.skip_binder().trait_ref.self_ty(),
+        |impl_def_id| {
+            if infcx.probe(|_| impl_may_apply(impl_def_id)) {
+                ambiguities.push(CandidateSource::DefId(impl_def_id))
+            }
+        },
+    );
+
+    let predicates =
+        tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
+    for (pred, span) in elaborate(tcx, predicates.into_iter()) {
+        let kind = pred.kind();
+        if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
+            && param_env_candidate_may_apply(kind.rebind(trait_pred))
+        {
+            if kind.rebind(trait_pred.trait_ref)
+                == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id()))
+            {
+                ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id())))
+            } else {
+                ambiguities.push(CandidateSource::ParamEnv(span))
+            }
+        }
+    }
+
+    ambiguities
+}
+
+#[extension(pub trait TypeErrCtxtAmbiguityExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    #[instrument(skip(self), level = "debug")]
+    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
+        // Unable to successfully determine, probably means
+        // insufficient type information, but could mean
+        // ambiguous impls. The latter *ought* to be a
+        // coherence violation, so we don't report it here.
+
+        let predicate = self.resolve_vars_if_possible(obligation.predicate);
+        let span = obligation.cause.span;
+
+        debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
+
+        // Ambiguity errors are often caused as fallout from earlier errors.
+        // We ignore them if this `infcx` is tainted in some cases below.
+
+        let bound_predicate = predicate.kind();
+        let mut err = match bound_predicate.skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
+                let trait_ref = bound_predicate.rebind(data.trait_ref);
+                debug!(?trait_ref);
+
+                if let Err(e) = predicate.error_reported() {
+                    return e;
+                }
+
+                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
+                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
+                    // other `Foo` impls are incoherent.
+                    return guar;
+                }
+
+                // This is kind of a hack: it frequently happens that some earlier
+                // error prevents types from being fully inferred, and then we get
+                // a bunch of uninteresting errors saying something like "<generic
+                // #0> doesn't implement Sized". It may even be true that we
+                // could just skip over all checks where the self-ty is an
+                // inference variable, but I was afraid that there might be an
+                // inference variable created, registered as an obligation, and
+                // then never forced by writeback, and hence by skipping here we'd
+                // be ignoring the fact that we don't KNOW the type works
+                // out. Though even that would probably be harmless, given that
+                // we're only talking about builtin traits, which are known to be
+                // inhabited. We used to check for `self.tcx.sess.has_errors()` to
+                // avoid inundating the user with unnecessary errors, but we now
+                // check upstream for type errors and don't add the obligations to
+                // begin with in those cases.
+                if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
+                    match self.tainted_by_errors() {
+                        None => {
+                            let err = self.emit_inference_failure_err(
+                                obligation.cause.body_id,
+                                span,
+                                trait_ref.self_ty().skip_binder().into(),
+                                TypeAnnotationNeeded::E0282,
+                                false,
+                            );
+                            return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
+                        }
+                        Some(e) => return e,
+                    }
+                }
+
+                // Typically, this ambiguity should only happen if
+                // there are unresolved type inference variables
+                // (otherwise it would suggest a coherence
+                // failure). But given #21974 that is not necessarily
+                // the case -- we can have multiple where clauses that
+                // are only distinguished by a region, which results
+                // in an ambiguity even when all types are fully
+                // known, since we don't dispatch based on region
+                // relationships.
+
+                // Pick the first generic parameter that still contains inference variables as the one
+                // we're going to emit an error for. If there are none (see above), fall back to
+                // a more general error.
+                let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
+
+                let mut err = if let Some(arg) = arg {
+                    self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        arg,
+                        TypeAnnotationNeeded::E0283,
+                        true,
+                    )
+                } else {
+                    struct_span_code_err!(
+                        self.dcx(),
+                        span,
+                        E0283,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    )
+                };
+
+                let mut ambiguities = compute_applicable_impls_for_diagnostics(
+                    self.infcx,
+                    &obligation.with(self.tcx, trait_ref),
+                );
+                let has_non_region_infer =
+                    trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
+                // It doesn't make sense to talk about applicable impls if there are more than a
+                // handful of them. If there are a lot of them, but only a few of them have no type
+                // params, we only show those, as they are more likely to be useful/intended.
+                if ambiguities.len() > 5 {
+                    let infcx = self.infcx;
+                    if !ambiguities.iter().all(|option| match option {
+                        CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
+                        CandidateSource::ParamEnv(_) => true,
+                    }) {
+                        // If not all are blanket impls, we filter blanked impls out.
+                        ambiguities.retain(|option| match option {
+                            CandidateSource::DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
+                            CandidateSource::ParamEnv(_) => true,
+                        });
+                    }
+                }
+                if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
+                    if let Some(e) = self.tainted_by_errors()
+                        && arg.is_none()
+                    {
+                        // If `arg.is_none()`, then this is probably two param-env
+                        // candidates or impl candidates that are equal modulo lifetimes.
+                        // Therefore, if we've already emitted an error, just skip this
+                        // one, since it's not particularly actionable.
+                        err.cancel();
+                        return e;
+                    }
+                    self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
+                } else {
+                    if let Some(e) = self.tainted_by_errors() {
+                        err.cancel();
+                        return e;
+                    }
+                    err.note(format!("cannot satisfy `{predicate}`"));
+                    let impl_candidates =
+                        self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
+                    if impl_candidates.len() < 40 {
+                        self.report_similar_impl_candidates(
+                            impl_candidates.as_slice(),
+                            trait_ref,
+                            obligation.cause.body_id,
+                            &mut err,
+                            false,
+                            obligation.param_env,
+                        );
+                    }
+                }
+
+                if let ObligationCauseCode::WhereClause(def_id, _)
+                | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
+                {
+                    self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
+                }
+
+                if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
+                    && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
+                {
+                    let mut expr_finder = FindExprBySpan::new(span, self.tcx);
+                    expr_finder.visit_expr(&body.value);
+
+                    if let Some(hir::Expr {
+                        kind:
+                            hir::ExprKind::Call(
+                                hir::Expr {
+                                    kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
+                                    ..
+                                },
+                                _,
+                            )
+                            | hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
+                        ..
+                    }) = expr_finder.result
+                        && let [
+                            ..,
+                            trait_path_segment @ hir::PathSegment {
+                                res: Res::Def(DefKind::Trait, trait_id),
+                                ..
+                            },
+                            hir::PathSegment {
+                                ident: assoc_item_name,
+                                res: Res::Def(_, item_id),
+                                ..
+                            },
+                        ] = path.segments
+                        && data.trait_ref.def_id == *trait_id
+                        && self.tcx.trait_of_item(*item_id) == Some(*trait_id)
+                        && let None = self.tainted_by_errors()
+                    {
+                        let (verb, noun) = match self.tcx.associated_item(item_id).kind {
+                            ty::AssocKind::Const => ("refer to the", "constant"),
+                            ty::AssocKind::Fn => ("call", "function"),
+                            // This is already covered by E0223, but this following single match
+                            // arm doesn't hurt here.
+                            ty::AssocKind::Type => ("refer to the", "type"),
+                        };
+
+                        // Replace the more general E0283 with a more specific error
+                        err.cancel();
+                        err = self.dcx().struct_span_err(
+                            span,
+                            format!(
+                                "cannot {verb} associated {noun} on trait without specifying the \
+                                 corresponding `impl` type",
+                            ),
+                        );
+                        err.code(E0790);
+
+                        if let Some(local_def_id) = data.trait_ref.def_id.as_local()
+                            && let hir::Node::Item(hir::Item {
+                                ident: trait_name,
+                                kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
+                                ..
+                            }) = self.tcx.hir_node_by_def_id(local_def_id)
+                            && let Some(method_ref) = trait_item_refs
+                                .iter()
+                                .find(|item_ref| item_ref.ident == *assoc_item_name)
+                        {
+                            err.span_label(
+                                method_ref.span,
+                                format!("`{trait_name}::{assoc_item_name}` defined here"),
+                            );
+                        }
+
+                        err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
+
+                        let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
+
+                        if let Some(impl_def_id) =
+                            trait_impls.non_blanket_impls().values().flatten().next()
+                        {
+                            let non_blanket_impl_count =
+                                trait_impls.non_blanket_impls().values().flatten().count();
+                            // If there is only one implementation of the trait, suggest using it.
+                            // Otherwise, use a placeholder comment for the implementation.
+                            let (message, self_type) = if non_blanket_impl_count == 1 {
+                                (
+                                    "use the fully-qualified path to the only available \
+                                     implementation",
+                                    format!(
+                                        "{}",
+                                        self.tcx.type_of(impl_def_id).instantiate_identity()
+                                    ),
+                                )
+                            } else {
+                                (
+                                    "use a fully-qualified path to a specific available \
+                                     implementation",
+                                    "/* self type */".to_string(),
+                                )
+                            };
+                            let mut suggestions =
+                                vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
+                            if let Some(generic_arg) = trait_path_segment.args {
+                                let between_span =
+                                    trait_path_segment.ident.span.between(generic_arg.span_ext);
+                                // get rid of :: between Trait and <type>
+                                // must be '::' between them, otherwise the parser won't accept the code
+                                suggestions.push((between_span, "".to_string()));
+                                suggestions
+                                    .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
+                            } else {
+                                suggestions.push((
+                                    trait_path_segment.ident.span.shrink_to_hi(),
+                                    ">".to_string(),
+                                ));
+                            }
+                            err.multipart_suggestion(
+                                message,
+                                suggestions,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
+                    }
+                };
+
+                err
+            }
+
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
+                // Same hacky approach as above to avoid deluging user
+                // with error messages.
+
+                if let Err(e) = arg.error_reported() {
+                    return e;
+                }
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    arg,
+                    TypeAnnotationNeeded::E0282,
+                    false,
+                )
+            }
+
+            ty::PredicateKind::Subtype(data) => {
+                if let Err(e) = data.error_reported() {
+                    return e;
+                }
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+                let ty::SubtypePredicate { a_is_expected: _, a, b } = data;
+                // both must be type variables, or the other would've been instantiated
+                assert!(a.is_ty_var() && b.is_ty_var());
+                self.emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    a.into(),
+                    TypeAnnotationNeeded::E0282,
+                    true,
+                )
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
+                if let Err(e) = predicate.error_reported() {
+                    return e;
+                }
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+
+                if let Err(guar) =
+                    self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
+                {
+                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
+                    // other `Foo` impls are incoherent.
+                    return guar;
+                }
+                let arg = data
+                    .projection_term
+                    .args
+                    .iter()
+                    .chain(Some(data.term.into_arg()))
+                    .find(|g| g.has_non_region_infer());
+                if let Some(arg) = arg {
+                    self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        arg,
+                        TypeAnnotationNeeded::E0284,
+                        true,
+                    )
+                    .with_note(format!("cannot satisfy `{predicate}`"))
+                } else {
+                    // If we can't find a generic parameter, just print a generic error
+                    struct_span_code_err!(
+                        self.dcx(),
+                        span,
+                        E0284,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    )
+                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
+                }
+            }
+
+            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
+                if let Err(e) = predicate.error_reported() {
+                    return e;
+                }
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+                let arg = data.walk().find(|g| g.is_non_region_infer());
+                if let Some(arg) = arg {
+                    let err = self.emit_inference_failure_err(
+                        obligation.cause.body_id,
+                        span,
+                        arg,
+                        TypeAnnotationNeeded::E0284,
+                        true,
+                    );
+                    err
+                } else {
+                    // If we can't find a generic parameter, just print a generic error
+                    struct_span_code_err!(
+                        self.dcx(),
+                        span,
+                        E0284,
+                        "type annotations needed: cannot satisfy `{}`",
+                        predicate,
+                    )
+                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
+                }
+            }
+
+            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
+                .emit_inference_failure_err(
+                    obligation.cause.body_id,
+                    span,
+                    ct.into(),
+                    TypeAnnotationNeeded::E0284,
+                    true,
+                ),
+            ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
+                if term.is_infer() =>
+            {
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+                struct_span_code_err!(
+                    self.dcx(),
+                    span,
+                    E0284,
+                    "type annotations needed: cannot normalize `{alias}`",
+                )
+                .with_span_label(span, format!("cannot normalize `{alias}`"))
+            }
+
+            _ => {
+                if let Some(e) = self.tainted_by_errors() {
+                    return e;
+                }
+                struct_span_code_err!(
+                    self.dcx(),
+                    span,
+                    E0284,
+                    "type annotations needed: cannot satisfy `{}`",
+                    predicate,
+                )
+                .with_span_label(span, format!("cannot satisfy `{predicate}`"))
+            }
+        };
+        self.note_obligation_cause(&mut err, obligation);
+        err.emit()
+    }
+
+    fn annotate_source_of_ambiguity(
+        &self,
+        err: &mut Diag<'_>,
+        ambiguities: &[CandidateSource],
+        predicate: ty::Predicate<'tcx>,
+    ) {
+        let mut spans = vec![];
+        let mut crates = vec![];
+        let mut post = vec![];
+        let mut has_param_env = false;
+        for ambiguity in ambiguities {
+            match ambiguity {
+                CandidateSource::DefId(impl_def_id) => match self.tcx.span_of_impl(*impl_def_id) {
+                    Ok(span) => spans.push(span),
+                    Err(name) => {
+                        crates.push(name);
+                        if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
+                            post.push(header);
+                        }
+                    }
+                },
+                CandidateSource::ParamEnv(span) => {
+                    has_param_env = true;
+                    spans.push(*span);
+                }
+            }
+        }
+        let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
+        crate_names.sort();
+        crate_names.dedup();
+        post.sort();
+        post.dedup();
+
+        if self.tainted_by_errors().is_some()
+            && (crate_names.len() == 1
+                && spans.len() == 0
+                && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
+                || predicate.visit_with(&mut HasNumericInferVisitor).is_break())
+        {
+            // Avoid complaining about other inference issues for expressions like
+            // `42 >> 1`, where the types are still `{integer}`, but we want to
+            // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
+            // NOTE(eddyb) this was `.cancel()`, but `err`
+            // is borrowed, so we can't fully defuse it.
+            err.downgrade_to_delayed_bug();
+            return;
+        }
+
+        let msg = format!(
+            "multiple `impl`s{} satisfying `{}` found",
+            if has_param_env { " or `where` clauses" } else { "" },
+            predicate
+        );
+        let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
+            format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
+        } else if post.len() == 1 {
+            format!(": `{}`", post[0])
+        } else {
+            String::new()
+        };
+
+        match (spans.len(), crates.len(), crate_names.len()) {
+            (0, 0, 0) => {
+                err.note(format!("cannot satisfy `{predicate}`"));
+            }
+            (0, _, 1) => {
+                err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
+            }
+            (0, _, _) => {
+                err.note(format!(
+                    "{} in the following crates: {}{}",
+                    msg,
+                    crate_names.join(", "),
+                    post,
+                ));
+            }
+            (_, 0, 0) => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+            }
+            (_, 1, 1) => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+                err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
+            }
+            _ => {
+                let span: MultiSpan = spans.into();
+                err.span_note(span, msg);
+                err.note(format!(
+                    "and more `impl`s found in the following crates: {}{}",
+                    crate_names.join(", "),
+                    post,
+                ));
+            }
+        }
+    }
+}
+
+struct HasNumericInferVisitor;
+
+impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
+    type Result = ControlFlow<()>;
+
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index d3096cf4b52..f7ec5f1ff32 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1,34 +1,30 @@
-// ignore-tidy-filelength :(
-
 use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote, TypeErrCtxtExt as _};
 use super::suggestions::{get_explanation_based_on_obligation, TypeErrCtxtExt as _};
+use crate::error_reporting::traits::infer_ctxt_ext::InferCtxtExt;
 use crate::errors::{
     AsyncClosureNotFn, ClosureFnMutLabel, ClosureFnOnceLabel, ClosureKindMismatch,
 };
-use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
+use crate::infer::error_reporting::TyCategory;
 use crate::infer::InferCtxtExt as _;
 use crate::infer::{self, InferCtxt};
-use crate::traits::error_reporting::infer_ctxt_ext::InferCtxtExt;
-use crate::traits::error_reporting::{ambiguity, ambiguity::CandidateSource::*};
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::specialize::to_pretty_impl_header;
 use crate::traits::NormalizeExt;
 use crate::traits::{
-    elaborate, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, Obligation,
-    ObligationCause, ObligationCauseCode, ObligationCtxt, Overflow, PredicateObligation,
-    SelectionError, SignatureMismatch, TraitNotObjectSafe,
+    elaborate, MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode,
+    ObligationCtxt, Overflow, PredicateObligation, SelectionError, SignatureMismatch,
+    TraitNotObjectSafe,
 };
 use core::ops::ControlFlow;
-use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
+use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
-use rustc_errors::{pluralize, struct_span_code_err, Applicability, MultiSpan, StringPart};
-use rustc_errors::{Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, StashKey};
-use rustc_hir::def::{DefKind, Namespace, Res};
+use rustc_errors::{pluralize, struct_span_code_err, Applicability, StringPart};
+use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
+use rustc_hir::def::Namespace;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
+use rustc_hir::Node;
 use rustc_hir::{self as hir, LangItem};
-use rustc_hir::{GenericParam, Item, Node};
 use rustc_infer::infer::error_reporting::TypeErrCtxt;
 use rustc_infer::infer::{InferOk, TypeTrace};
 use rustc_macros::extension;
@@ -42,323 +38,21 @@ use rustc_middle::ty::print::{
     PrintTraitRefExt as _,
 };
 use rustc_middle::ty::{
-    self, SubtypePredicate, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitable,
-    TypeVisitableExt, Upcast,
+    self, ToPolyTraitRef, TraitRef, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, Upcast,
 };
 use rustc_middle::{bug, span_bug};
-use rustc_session::Limit;
-use rustc_span::def_id::LOCAL_CRATE;
 use rustc_span::symbol::sym;
-use rustc_span::{BytePos, ExpnKind, Span, Symbol, DUMMY_SP};
+use rustc_span::{BytePos, Span, Symbol, DUMMY_SP};
 use std::borrow::Cow;
-use std::fmt;
-use std::iter;
 
 use super::{
-    ArgKind, CandidateSimilarity, FindExprBySpan, FindTypeParam, GetSafeTransmuteErrorAndReason,
-    HasNumericInferVisitor, ImplCandidate, UnsatisfiedConst,
+    ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst,
 };
 
 pub use rustc_infer::traits::error_reporting::*;
 
-pub enum OverflowCause<'tcx> {
-    DeeplyNormalize(ty::AliasTerm<'tcx>),
-    TraitSolver(ty::Predicate<'tcx>),
-}
-
-pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
-    tcx: TyCtxt<'tcx>,
-    err: &mut Diag<'_, G>,
-) {
-    let suggested_limit = match tcx.recursion_limit() {
-        Limit(0) => Limit(2),
-        limit => limit * 2,
-    };
-    err.help(format!(
-        "consider increasing the recursion limit by adding a \
-         `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
-        suggested_limit,
-        tcx.crate_name(LOCAL_CRATE),
-    ));
-}
-
-#[extension(pub trait TypeErrCtxtExt<'tcx>)]
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
-    fn report_fulfillment_errors(
-        &self,
-        mut errors: Vec<FulfillmentError<'tcx>>,
-    ) -> ErrorGuaranteed {
-        self.sub_relations
-            .borrow_mut()
-            .add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
-
-        #[derive(Debug)]
-        struct ErrorDescriptor<'tcx> {
-            predicate: ty::Predicate<'tcx>,
-            index: Option<usize>, // None if this is an old error
-        }
-
-        let mut error_map: FxIndexMap<_, Vec<_>> = self
-            .reported_trait_errors
-            .borrow()
-            .iter()
-            .map(|(&span, predicates)| {
-                (
-                    span,
-                    predicates
-                        .0
-                        .iter()
-                        .map(|&predicate| ErrorDescriptor { predicate, index: None })
-                        .collect(),
-                )
-            })
-            .collect();
-
-        // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
-        // with more relevant type information and hide redundant E0282 errors.
-        errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
-                if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
-            {
-                1
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
-            ty::PredicateKind::Coerce(_) => 2,
-            _ => 0,
-        });
-
-        for (index, error) in errors.iter().enumerate() {
-            // We want to ignore desugarings here: spans are equivalent even
-            // if one is the result of a desugaring and the other is not.
-            let mut span = error.obligation.cause.span;
-            let expn_data = span.ctxt().outer_expn_data();
-            if let ExpnKind::Desugaring(_) = expn_data.kind {
-                span = expn_data.call_site;
-            }
-
-            error_map.entry(span).or_default().push(ErrorDescriptor {
-                predicate: error.obligation.predicate,
-                index: Some(index),
-            });
-        }
-
-        // We do this in 2 passes because we want to display errors in order, though
-        // maybe it *is* better to sort errors by span or something.
-        let mut is_suppressed = vec![false; errors.len()];
-        for (_, error_set) in error_map.iter() {
-            // We want to suppress "duplicate" errors with the same span.
-            for error in error_set {
-                if let Some(index) = error.index {
-                    // Suppress errors that are either:
-                    // 1) strictly implied by another error.
-                    // 2) implied by an error with a smaller index.
-                    for error2 in error_set {
-                        if error2.index.is_some_and(|index2| is_suppressed[index2]) {
-                            // Avoid errors being suppressed by already-suppressed
-                            // errors, to prevent all errors from being suppressed
-                            // at once.
-                            continue;
-                        }
-
-                        if self.error_implies(error2.predicate, error.predicate)
-                            && !(error2.index >= error.index
-                                && self.error_implies(error.predicate, error2.predicate))
-                        {
-                            info!("skipping {:?} (implied by {:?})", error, error2);
-                            is_suppressed[index] = true;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-
-        let mut reported = None;
-
-        for from_expansion in [false, true] {
-            for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
-                if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
-                    let guar = self.report_fulfillment_error(error);
-                    self.infcx.set_tainted_by_errors(guar);
-                    reported = Some(guar);
-                    // We want to ignore desugarings here: spans are equivalent even
-                    // if one is the result of a desugaring and the other is not.
-                    let mut span = error.obligation.cause.span;
-                    let expn_data = span.ctxt().outer_expn_data();
-                    if let ExpnKind::Desugaring(_) = expn_data.kind {
-                        span = expn_data.call_site;
-                    }
-                    self.reported_trait_errors
-                        .borrow_mut()
-                        .entry(span)
-                        .or_insert_with(|| (vec![], guar))
-                        .0
-                        .push(error.obligation.predicate);
-                }
-            }
-        }
-
-        // It could be that we don't report an error because we have seen an `ErrorReported` from
-        // another source. We should probably be able to fix most of these, but some are delayed
-        // bugs that get a proper error after this function.
-        reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
-    }
-
-    /// Reports that an overflow has occurred and halts compilation. We
-    /// halt compilation unconditionally because it is important that
-    /// overflows never be masked -- they basically represent computations
-    /// whose result could not be truly determined and thus we can't say
-    /// if the program type checks or not -- and they are unusual
-    /// occurrences in any case.
-    fn report_overflow_error(
-        &self,
-        cause: OverflowCause<'tcx>,
-        span: Span,
-        suggest_increasing_limit: bool,
-        mutate: impl FnOnce(&mut Diag<'_>),
-    ) -> ! {
-        let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit);
-        mutate(&mut err);
-        err.emit();
-        FatalError.raise();
-    }
-
-    fn build_overflow_error(
-        &self,
-        cause: OverflowCause<'tcx>,
-        span: Span,
-        suggest_increasing_limit: bool,
-    ) -> Diag<'tcx> {
-        fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String
-        where
-            T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
-        {
-            let s = value.to_string();
-            if s.len() > 50 {
-                // We don't need to save the type to a file, we will be talking about this type already
-                // in a separate note when we explain the obligation, so it will be available that way.
-                let mut cx: FmtPrinter<'_, '_> =
-                    FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
-                value.print(&mut cx).unwrap();
-                cx.into_buffer()
-            } else {
-                s
-            }
-        }
-
-        let mut err = match cause {
-            OverflowCause::DeeplyNormalize(alias_term) => {
-                let alias_term = self.resolve_vars_if_possible(alias_term);
-                let kind = alias_term.kind(self.tcx).descr();
-                let alias_str = with_short_path(self.tcx, alias_term);
-                struct_span_code_err!(
-                    self.dcx(),
-                    span,
-                    E0275,
-                    "overflow normalizing the {kind} `{alias_str}`",
-                )
-            }
-            OverflowCause::TraitSolver(predicate) => {
-                let predicate = self.resolve_vars_if_possible(predicate);
-                match predicate.kind().skip_binder() {
-                    ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ })
-                    | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
-                        struct_span_code_err!(
-                            self.dcx(),
-                            span,
-                            E0275,
-                            "overflow assigning `{a}` to `{b}`",
-                        )
-                    }
-                    _ => {
-                        let pred_str = with_short_path(self.tcx, predicate);
-                        struct_span_code_err!(
-                            self.dcx(),
-                            span,
-                            E0275,
-                            "overflow evaluating the requirement `{pred_str}`",
-                        )
-                    }
-                }
-            }
-        };
-
-        if suggest_increasing_limit {
-            suggest_new_overflow_limit(self.tcx, &mut err);
-        }
-
-        err
-    }
-
-    /// Reports that an overflow has occurred and halts compilation. We
-    /// halt compilation unconditionally because it is important that
-    /// overflows never be masked -- they basically represent computations
-    /// whose result could not be truly determined and thus we can't say
-    /// if the program type checks or not -- and they are unusual
-    /// occurrences in any case.
-    fn report_overflow_obligation<T>(
-        &self,
-        obligation: &Obligation<'tcx, T>,
-        suggest_increasing_limit: bool,
-    ) -> !
-    where
-        T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone,
-    {
-        let predicate = obligation.predicate.clone().upcast(self.tcx);
-        let predicate = self.resolve_vars_if_possible(predicate);
-        self.report_overflow_error(
-            OverflowCause::TraitSolver(predicate),
-            obligation.cause.span,
-            suggest_increasing_limit,
-            |err| {
-                self.note_obligation_cause_code(
-                    obligation.cause.body_id,
-                    err,
-                    predicate,
-                    obligation.param_env,
-                    obligation.cause.code(),
-                    &mut vec![],
-                    &mut Default::default(),
-                );
-            },
-        );
-    }
-
-    /// Reports that a cycle was detected which led to overflow and halts
-    /// compilation. This is equivalent to `report_overflow_obligation` except
-    /// that we can give a more helpful error message (and, in particular,
-    /// we do not suggest increasing the overflow limit, which is not
-    /// going to help).
-    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
-        let cycle = self.resolve_vars_if_possible(cycle.to_owned());
-        assert!(!cycle.is_empty());
-
-        debug!(?cycle, "report_overflow_error_cycle");
-
-        // The 'deepest' obligation is most likely to have a useful
-        // cause 'backtrace'
-        self.report_overflow_obligation(
-            cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
-            false,
-        );
-    }
-
-    fn report_overflow_no_abort(
-        &self,
-        obligation: PredicateObligation<'tcx>,
-        suggest_increasing_limit: bool,
-    ) -> ErrorGuaranteed {
-        let obligation = self.resolve_vars_if_possible(obligation);
-        let mut err = self.build_overflow_error(
-            OverflowCause::TraitSolver(obligation.predicate),
-            obligation.cause.span,
-            suggest_increasing_limit,
-        );
-        self.note_obligation_cause(&mut err, &obligation);
-        self.point_at_returns_when_relevant(&mut err, &obligation);
-        err.emit()
-    }
-
+#[extension(pub trait TypeErrCtxtSelectionErrExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     /// The `root_obligation` parameter should be the `root_obligation` field
     /// from a `FulfillmentError`. If no `FulfillmentError` is available,
     /// then it should be the same as `obligation`.
@@ -986,7 +680,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         self.point_at_returns_when_relevant(&mut err, &obligation);
         err.emit()
     }
+}
 
+#[extension(pub(super) trait TypeErrCtxtExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn apply_do_not_recommend(&self, obligation: &mut PredicateObligation<'tcx>) -> bool {
         let mut base_cause = obligation.cause.code().clone();
         let mut applied_do_not_recommend = false;
@@ -1073,7 +770,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             // mismatched, then we have a totally different error to report.
             if self.enter_forall(found_args, |found_args| {
                 self.enter_forall(expected_args, |expected_args| {
-                    !self.can_sub(obligation.param_env, expected_args, found_args)
+                    !self.can_eq(obligation.param_env, expected_args, found_args)
                 })
             }) {
                 return None;
@@ -1101,7 +798,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 && let ty::FnPtr(sig) = by_ref_captures.kind()
                 && !sig.skip_binder().output().is_unit()
             {
-                let mut err = self.tcx.dcx().create_err(AsyncClosureNotFn {
+                let mut err = self.dcx().create_err(AsyncClosureNotFn {
                     span: self.tcx.def_span(closure_def_id),
                     kind: expected_kind.as_str(),
                 });
@@ -1134,7 +831,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     }
 
     /// When the `E` of the resulting `Result<T, E>` in an expression `foo().bar().baz()?`,
-    /// identify thoe method chain sub-expressions that could or could not have been annotated
+    /// identify those method chain sub-expressions that could or could not have been annotated
     /// with `?`.
     fn try_conversion_context(
         &self,
@@ -1296,7 +993,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     expr = binding_expr;
                 }
                 if let hir::Node::Param(_param) = parent {
-                    // ...and it is a an fn argument.
+                    // ...and it is an fn argument.
                     break;
                 }
             }
@@ -1351,7 +1048,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         ty: Ty<'tcx>,
         obligation: &PredicateObligation<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let span = obligation.cause.span;
 
         let mut diag = match ty.kind() {
@@ -1445,8 +1142,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     }
 }
 
-#[extension(pub(super) trait InferCtxtPrivExt<'tcx>)]
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+#[extension(pub(super) trait InferCtxtPrivExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn can_match_trait(
         &self,
         goal: ty::TraitPredicate<'tcx>,
@@ -1507,72 +1204,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    #[instrument(skip(self), level = "debug")]
-    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
-        let mut error = FulfillmentError {
-            obligation: error.obligation.clone(),
-            code: error.code.clone(),
-            root_obligation: error.root_obligation.clone(),
-        };
-        if matches!(
-            error.code,
-            FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
-                | FulfillmentErrorCode::Project(_)
-        ) && self.apply_do_not_recommend(&mut error.obligation)
-        {
-            error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
-        }
-
-        match error.code {
-            FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
-                error.obligation.clone(),
-                &error.root_obligation,
-                selection_error,
-            ),
-            FulfillmentErrorCode::Project(ref e) => {
-                self.report_projection_error(&error.obligation, e)
-            }
-            FulfillmentErrorCode::Ambiguity { overflow: None } => {
-                self.maybe_report_ambiguity(&error.obligation)
-            }
-            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
-                self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
-            }
-            FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
-                .report_mismatched_types(
-                    &error.obligation.cause,
-                    expected_found.expected,
-                    expected_found.found,
-                    *err,
-                )
-                .emit(),
-            FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
-                let mut diag = self.report_mismatched_consts(
-                    &error.obligation.cause,
-                    expected_found.expected,
-                    expected_found.found,
-                    *err,
-                );
-                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
-                if let ObligationCauseCode::WhereClause(..)
-                | ObligationCauseCode::WhereClauseInExpr(..) = code
-                {
-                    self.note_obligation_cause_code(
-                        error.obligation.cause.body_id,
-                        &mut diag,
-                        error.obligation.predicate,
-                        error.obligation.param_env,
-                        code,
-                        &mut vec![],
-                        &mut Default::default(),
-                    );
-                }
-                diag.emit()
-            }
-            FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
-        }
-    }
-
     #[instrument(level = "debug", skip_all)]
     fn report_projection_error(
         &self,
@@ -1586,60 +1217,113 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         self.probe(|_| {
-            let ocx = ObligationCtxt::new(self);
-
             // try to find the mismatched types to report the error with.
             //
             // this can fail if the problem was higher-ranked, in which
             // cause I have no idea for a good error message.
             let bound_predicate = predicate.kind();
-            let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) =
-                bound_predicate.skip_binder()
-            {
-                let data = self.instantiate_binder_with_fresh_vars(
-                    obligation.cause.span,
-                    infer::BoundRegionConversionTime::HigherRankedType,
-                    bound_predicate.rebind(data),
-                );
-                let unnormalized_term = data.projection_term.to_term(self.tcx);
-                // FIXME(-Znext-solver): For diagnostic purposes, it would be nice
-                // to deeply normalize this type.
-                let normalized_term =
-                    ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
-
-                debug!(?obligation.cause, ?obligation.param_env);
-
-                debug!(?normalized_term, data.ty = ?data.term);
+            let (values, err) = match bound_predicate.skip_binder() {
+                ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
+                    let ocx = ObligationCtxt::new(self);
+
+                    let data = self.instantiate_binder_with_fresh_vars(
+                        obligation.cause.span,
+                        infer::BoundRegionConversionTime::HigherRankedType,
+                        bound_predicate.rebind(data),
+                    );
+                    let unnormalized_term = data.projection_term.to_term(self.tcx);
+                    // FIXME(-Znext-solver): For diagnostic purposes, it would be nice
+                    // to deeply normalize this type.
+                    let normalized_term =
+                        ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
+
+                    let is_normalized_term_expected = !matches!(
+                        obligation.cause.code().peel_derives(),
+                        ObligationCauseCode::WhereClause(..)
+                            | ObligationCauseCode::WhereClauseInExpr(..)
+                            | ObligationCauseCode::Coercion { .. }
+                    );
 
-                let is_normalized_term_expected = !matches!(
-                    obligation.cause.code().peel_derives(),
-                    |ObligationCauseCode::WhereClause(..)| ObligationCauseCode::WhereClauseInExpr(
-                        ..
-                    ) | ObligationCauseCode::Coercion { .. }
-                );
+                    let (expected, actual) = if is_normalized_term_expected {
+                        (normalized_term, data.term)
+                    } else {
+                        (data.term, normalized_term)
+                    };
 
-                let (expected, actual) = if is_normalized_term_expected {
-                    (normalized_term, data.term)
-                } else {
-                    (data.term, normalized_term)
-                };
+                    // constrain inference variables a bit more to nested obligations from normalize so
+                    // we can have more helpful errors.
+                    //
+                    // we intentionally drop errors from normalization here,
+                    // since the normalization is just done to improve the error message.
+                    let _ = ocx.select_where_possible();
 
-                // constrain inference variables a bit more to nested obligations from normalize so
-                // we can have more helpful errors.
-                //
-                // we intentionally drop errors from normalization here,
-                // since the normalization is just done to improve the error message.
-                let _ = ocx.select_where_possible();
+                    if let Err(new_err) =
+                        ocx.eq(&obligation.cause, obligation.param_env, expected, actual)
+                    {
+                        (
+                            Some((
+                                data.projection_term,
+                                is_normalized_term_expected,
+                                self.resolve_vars_if_possible(normalized_term),
+                                data.term,
+                            )),
+                            new_err,
+                        )
+                    } else {
+                        (None, error.err)
+                    }
+                }
+                ty::PredicateKind::AliasRelate(lhs, rhs, _) => {
+                    let derive_better_type_error =
+                        |alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| {
+                            let ocx = ObligationCtxt::new(self);
+                            let normalized_term = match expected_term.unpack() {
+                                ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(),
+                                ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
+                            };
+                            ocx.register_obligation(Obligation::new(
+                                self.tcx,
+                                ObligationCause::dummy(),
+                                obligation.param_env,
+                                ty::PredicateKind::NormalizesTo(ty::NormalizesTo {
+                                    alias: alias_term,
+                                    term: normalized_term,
+                                }),
+                            ));
+                            let _ = ocx.select_where_possible();
+                            if let Err(terr) = ocx.eq(
+                                &ObligationCause::dummy(),
+                                obligation.param_env,
+                                expected_term,
+                                normalized_term,
+                            ) {
+                                Some((terr, self.resolve_vars_if_possible(normalized_term)))
+                            } else {
+                                None
+                            }
+                        };
 
-                if let Err(new_err) =
-                    ocx.eq(&obligation.cause, obligation.param_env, expected, actual)
-                {
-                    (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err)
-                } else {
-                    (None, error.err)
+                    if let Some(lhs) = lhs.to_alias_term()
+                        && let Some((better_type_err, expected_term)) =
+                            derive_better_type_error(lhs, rhs)
+                    {
+                        (
+                            Some((lhs, true, self.resolve_vars_if_possible(expected_term), rhs)),
+                            better_type_err,
+                        )
+                    } else if let Some(rhs) = rhs.to_alias_term()
+                        && let Some((better_type_err, expected_term)) =
+                            derive_better_type_error(rhs, lhs)
+                    {
+                        (
+                            Some((rhs, true, self.resolve_vars_if_possible(expected_term), lhs)),
+                            better_type_err,
+                        )
+                    } else {
+                        (None, error.err)
+                    }
                 }
-            } else {
-                (None, error.err)
+                _ => (None, error.err),
             };
 
             let msg = values
@@ -1737,15 +1421,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn maybe_detailed_projection_msg(
         &self,
-        pred: ty::ProjectionPredicate<'tcx>,
+        projection_term: ty::AliasTerm<'tcx>,
         normalized_ty: ty::Term<'tcx>,
         expected_ty: ty::Term<'tcx>,
     ) -> Option<String> {
-        let trait_def_id = pred.projection_term.trait_def_id(self.tcx);
-        let self_ty = pred.projection_term.self_ty();
+        let trait_def_id = projection_term.trait_def_id(self.tcx);
+        let self_ty = projection_term.self_ty();
 
         with_forced_trimmed_paths! {
-            if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) {
+            if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) {
                 let fn_kind = self_ty.prefix_string(self.tcx);
                 let item = match self_ty.kind() {
                     ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
@@ -2297,536 +1981,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
     }
 
-    #[instrument(skip(self), level = "debug")]
-    fn maybe_report_ambiguity(&self, obligation: &PredicateObligation<'tcx>) -> ErrorGuaranteed {
-        // Unable to successfully determine, probably means
-        // insufficient type information, but could mean
-        // ambiguous impls. The latter *ought* to be a
-        // coherence violation, so we don't report it here.
-
-        let predicate = self.resolve_vars_if_possible(obligation.predicate);
-        let span = obligation.cause.span;
-
-        debug!(?predicate, obligation.cause.code = ?obligation.cause.code());
-
-        // Ambiguity errors are often caused as fallout from earlier errors.
-        // We ignore them if this `infcx` is tainted in some cases below.
-
-        let bound_predicate = predicate.kind();
-        let mut err = match bound_predicate.skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(data)) => {
-                let trait_ref = bound_predicate.rebind(data.trait_ref);
-                debug!(?trait_ref);
-
-                if let Err(e) = predicate.error_reported() {
-                    return e;
-                }
-
-                if let Err(guar) = self.tcx.ensure().coherent_trait(trait_ref.def_id()) {
-                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
-                    // other `Foo` impls are incoherent.
-                    return guar;
-                }
-
-                // This is kind of a hack: it frequently happens that some earlier
-                // error prevents types from being fully inferred, and then we get
-                // a bunch of uninteresting errors saying something like "<generic
-                // #0> doesn't implement Sized". It may even be true that we
-                // could just skip over all checks where the self-ty is an
-                // inference variable, but I was afraid that there might be an
-                // inference variable created, registered as an obligation, and
-                // then never forced by writeback, and hence by skipping here we'd
-                // be ignoring the fact that we don't KNOW the type works
-                // out. Though even that would probably be harmless, given that
-                // we're only talking about builtin traits, which are known to be
-                // inhabited. We used to check for `self.tcx.sess.has_errors()` to
-                // avoid inundating the user with unnecessary errors, but we now
-                // check upstream for type errors and don't add the obligations to
-                // begin with in those cases.
-                if self.tcx.is_lang_item(trait_ref.def_id(), LangItem::Sized) {
-                    match self.tainted_by_errors() {
-                        None => {
-                            let err = self.emit_inference_failure_err(
-                                obligation.cause.body_id,
-                                span,
-                                trait_ref.self_ty().skip_binder().into(),
-                                ErrorCode::E0282,
-                                false,
-                            );
-                            return err.stash(span, StashKey::MaybeForgetReturn).unwrap();
-                        }
-                        Some(e) => return e,
-                    }
-                }
-
-                // Typically, this ambiguity should only happen if
-                // there are unresolved type inference variables
-                // (otherwise it would suggest a coherence
-                // failure). But given #21974 that is not necessarily
-                // the case -- we can have multiple where clauses that
-                // are only distinguished by a region, which results
-                // in an ambiguity even when all types are fully
-                // known, since we don't dispatch based on region
-                // relationships.
-
-                // Pick the first generic parameter that still contains inference variables as the one
-                // we're going to emit an error for. If there are none (see above), fall back to
-                // a more general error.
-                let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer());
-
-                let mut err = if let Some(arg) = arg {
-                    self.emit_inference_failure_err(
-                        obligation.cause.body_id,
-                        span,
-                        arg,
-                        ErrorCode::E0283,
-                        true,
-                    )
-                } else {
-                    struct_span_code_err!(
-                        self.dcx(),
-                        span,
-                        E0283,
-                        "type annotations needed: cannot satisfy `{}`",
-                        predicate,
-                    )
-                };
-
-                let mut ambiguities = ambiguity::compute_applicable_impls_for_diagnostics(
-                    self.infcx,
-                    &obligation.with(self.tcx, trait_ref),
-                );
-                let has_non_region_infer =
-                    trait_ref.skip_binder().args.types().any(|t| !t.is_ty_or_numeric_infer());
-                // It doesn't make sense to talk about applicable impls if there are more than a
-                // handful of them. If there are a lot of them, but only a few of them have no type
-                // params, we only show those, as they are more likely to be useful/intended.
-                if ambiguities.len() > 5 {
-                    let infcx = self.infcx;
-                    if !ambiguities.iter().all(|option| match option {
-                        DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
-                        ParamEnv(_) => true,
-                    }) {
-                        // If not all are blanket impls, we filter blanked impls out.
-                        ambiguities.retain(|option| match option {
-                            DefId(did) => infcx.tcx.generics_of(*did).count() == 0,
-                            ParamEnv(_) => true,
-                        });
-                    }
-                }
-                if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
-                    if let Some(e) = self.tainted_by_errors()
-                        && arg.is_none()
-                    {
-                        // If `arg.is_none()`, then this is probably two param-env
-                        // candidates or impl candidates that are equal modulo lifetimes.
-                        // Therefore, if we've already emitted an error, just skip this
-                        // one, since it's not particularly actionable.
-                        err.cancel();
-                        return e;
-                    }
-                    self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
-                } else {
-                    if let Some(e) = self.tainted_by_errors() {
-                        err.cancel();
-                        return e;
-                    }
-                    err.note(format!("cannot satisfy `{predicate}`"));
-                    let impl_candidates =
-                        self.find_similar_impl_candidates(predicate.as_trait_clause().unwrap());
-                    if impl_candidates.len() < 40 {
-                        self.report_similar_impl_candidates(
-                            impl_candidates.as_slice(),
-                            trait_ref,
-                            obligation.cause.body_id,
-                            &mut err,
-                            false,
-                            obligation.param_env,
-                        );
-                    }
-                }
-
-                if let ObligationCauseCode::WhereClause(def_id, _)
-                | ObligationCauseCode::WhereClauseInExpr(def_id, ..) = *obligation.cause.code()
-                {
-                    self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
-                }
-
-                if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack())
-                    && let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id)
-                {
-                    let mut expr_finder = FindExprBySpan::new(span, self.tcx);
-                    expr_finder.visit_expr(&body.value);
-
-                    if let Some(hir::Expr {
-                        kind:
-                            hir::ExprKind::Call(
-                                hir::Expr {
-                                    kind: hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
-                                    ..
-                                },
-                                _,
-                            )
-                            | hir::ExprKind::Path(hir::QPath::Resolved(None, path)),
-                        ..
-                    }) = expr_finder.result
-                        && let [
-                            ..,
-                            trait_path_segment @ hir::PathSegment {
-                                res: Res::Def(DefKind::Trait, trait_id),
-                                ..
-                            },
-                            hir::PathSegment {
-                                ident: assoc_item_name,
-                                res: Res::Def(_, item_id),
-                                ..
-                            },
-                        ] = path.segments
-                        && data.trait_ref.def_id == *trait_id
-                        && self.tcx.trait_of_item(*item_id) == Some(*trait_id)
-                        && let None = self.tainted_by_errors()
-                    {
-                        let (verb, noun) = match self.tcx.associated_item(item_id).kind {
-                            ty::AssocKind::Const => ("refer to the", "constant"),
-                            ty::AssocKind::Fn => ("call", "function"),
-                            // This is already covered by E0223, but this following single match
-                            // arm doesn't hurt here.
-                            ty::AssocKind::Type => ("refer to the", "type"),
-                        };
-
-                        // Replace the more general E0283 with a more specific error
-                        err.cancel();
-                        err = self.dcx().struct_span_err(
-                            span,
-                            format!(
-                                "cannot {verb} associated {noun} on trait without specifying the \
-                                 corresponding `impl` type",
-                            ),
-                        );
-                        err.code(E0790);
-
-                        if let Some(local_def_id) = data.trait_ref.def_id.as_local()
-                            && let hir::Node::Item(hir::Item {
-                                ident: trait_name,
-                                kind: hir::ItemKind::Trait(_, _, _, _, trait_item_refs),
-                                ..
-                            }) = self.tcx.hir_node_by_def_id(local_def_id)
-                            && let Some(method_ref) = trait_item_refs
-                                .iter()
-                                .find(|item_ref| item_ref.ident == *assoc_item_name)
-                        {
-                            err.span_label(
-                                method_ref.span,
-                                format!("`{trait_name}::{assoc_item_name}` defined here"),
-                            );
-                        }
-
-                        err.span_label(span, format!("cannot {verb} associated {noun} of trait"));
-
-                        let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
-
-                        if let Some(impl_def_id) =
-                            trait_impls.non_blanket_impls().values().flatten().next()
-                        {
-                            let non_blanket_impl_count =
-                                trait_impls.non_blanket_impls().values().flatten().count();
-                            // If there is only one implementation of the trait, suggest using it.
-                            // Otherwise, use a placeholder comment for the implementation.
-                            let (message, self_type) = if non_blanket_impl_count == 1 {
-                                (
-                                    "use the fully-qualified path to the only available \
-                                     implementation",
-                                    format!(
-                                        "{}",
-                                        self.tcx.type_of(impl_def_id).instantiate_identity()
-                                    ),
-                                )
-                            } else {
-                                (
-                                    "use a fully-qualified path to a specific available \
-                                     implementation",
-                                    "/* self type */".to_string(),
-                                )
-                            };
-                            let mut suggestions =
-                                vec![(path.span.shrink_to_lo(), format!("<{self_type} as "))];
-                            if let Some(generic_arg) = trait_path_segment.args {
-                                let between_span =
-                                    trait_path_segment.ident.span.between(generic_arg.span_ext);
-                                // get rid of :: between Trait and <type>
-                                // must be '::' between them, otherwise the parser won't accept the code
-                                suggestions.push((between_span, "".to_string()));
-                                suggestions
-                                    .push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
-                            } else {
-                                suggestions.push((
-                                    trait_path_segment.ident.span.shrink_to_hi(),
-                                    ">".to_string(),
-                                ));
-                            }
-                            err.multipart_suggestion(
-                                message,
-                                suggestions,
-                                Applicability::MaybeIncorrect,
-                            );
-                        }
-                    }
-                };
-
-                err
-            }
-
-            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => {
-                // Same hacky approach as above to avoid deluging user
-                // with error messages.
-
-                if let Err(e) = arg.error_reported() {
-                    return e;
-                }
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-
-                self.emit_inference_failure_err(
-                    obligation.cause.body_id,
-                    span,
-                    arg,
-                    ErrorCode::E0282,
-                    false,
-                )
-            }
-
-            ty::PredicateKind::Subtype(data) => {
-                if let Err(e) = data.error_reported() {
-                    return e;
-                }
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-                let SubtypePredicate { a_is_expected: _, a, b } = data;
-                // both must be type variables, or the other would've been instantiated
-                assert!(a.is_ty_var() && b.is_ty_var());
-                self.emit_inference_failure_err(
-                    obligation.cause.body_id,
-                    span,
-                    a.into(),
-                    ErrorCode::E0282,
-                    true,
-                )
-            }
-            ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
-                if let Err(e) = predicate.error_reported() {
-                    return e;
-                }
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-
-                if let Err(guar) =
-                    self.tcx.ensure().coherent_trait(self.tcx.parent(data.projection_term.def_id))
-                {
-                    // Avoid bogus "type annotations needed `Foo: Bar`" errors on `impl Bar for Foo` in case
-                    // other `Foo` impls are incoherent.
-                    return guar;
-                }
-                let arg = data
-                    .projection_term
-                    .args
-                    .iter()
-                    .chain(Some(data.term.into_arg()))
-                    .find(|g| g.has_non_region_infer());
-                if let Some(arg) = arg {
-                    self.emit_inference_failure_err(
-                        obligation.cause.body_id,
-                        span,
-                        arg,
-                        ErrorCode::E0284,
-                        true,
-                    )
-                    .with_note(format!("cannot satisfy `{predicate}`"))
-                } else {
-                    // If we can't find a generic parameter, just print a generic error
-                    struct_span_code_err!(
-                        self.dcx(),
-                        span,
-                        E0284,
-                        "type annotations needed: cannot satisfy `{}`",
-                        predicate,
-                    )
-                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
-                }
-            }
-
-            ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(data)) => {
-                if let Err(e) = predicate.error_reported() {
-                    return e;
-                }
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-                let arg = data.walk().find(|g| g.is_non_region_infer());
-                if let Some(arg) = arg {
-                    let err = self.emit_inference_failure_err(
-                        obligation.cause.body_id,
-                        span,
-                        arg,
-                        ErrorCode::E0284,
-                        true,
-                    );
-                    err
-                } else {
-                    // If we can't find a generic parameter, just print a generic error
-                    struct_span_code_err!(
-                        self.dcx(),
-                        span,
-                        E0284,
-                        "type annotations needed: cannot satisfy `{}`",
-                        predicate,
-                    )
-                    .with_span_label(span, format!("cannot satisfy `{predicate}`"))
-                }
-            }
-
-            ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ..)) => self
-                .emit_inference_failure_err(
-                    obligation.cause.body_id,
-                    span,
-                    ct.into(),
-                    ErrorCode::E0284,
-                    true,
-                ),
-            ty::PredicateKind::NormalizesTo(ty::NormalizesTo { alias, term })
-                if term.is_infer() =>
-            {
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-                struct_span_code_err!(
-                    self.dcx(),
-                    span,
-                    E0284,
-                    "type annotations needed: cannot normalize `{alias}`",
-                )
-                .with_span_label(span, format!("cannot normalize `{alias}`"))
-            }
-
-            _ => {
-                if let Some(e) = self.tainted_by_errors() {
-                    return e;
-                }
-                struct_span_code_err!(
-                    self.dcx(),
-                    span,
-                    E0284,
-                    "type annotations needed: cannot satisfy `{}`",
-                    predicate,
-                )
-                .with_span_label(span, format!("cannot satisfy `{predicate}`"))
-            }
-        };
-        self.note_obligation_cause(&mut err, obligation);
-        err.emit()
-    }
-
-    fn annotate_source_of_ambiguity(
-        &self,
-        err: &mut Diag<'_>,
-        ambiguities: &[ambiguity::CandidateSource],
-        predicate: ty::Predicate<'tcx>,
-    ) {
-        let mut spans = vec![];
-        let mut crates = vec![];
-        let mut post = vec![];
-        let mut has_param_env = false;
-        for ambiguity in ambiguities {
-            match ambiguity {
-                ambiguity::CandidateSource::DefId(impl_def_id) => {
-                    match self.tcx.span_of_impl(*impl_def_id) {
-                        Ok(span) => spans.push(span),
-                        Err(name) => {
-                            crates.push(name);
-                            if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
-                                post.push(header);
-                            }
-                        }
-                    }
-                }
-                ambiguity::CandidateSource::ParamEnv(span) => {
-                    has_param_env = true;
-                    spans.push(*span);
-                }
-            }
-        }
-        let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{n}`")).collect();
-        crate_names.sort();
-        crate_names.dedup();
-        post.sort();
-        post.dedup();
-
-        if self.tainted_by_errors().is_some()
-            && (crate_names.len() == 1
-                && spans.len() == 0
-                && ["`core`", "`alloc`", "`std`"].contains(&crate_names[0].as_str())
-                || predicate.visit_with(&mut HasNumericInferVisitor).is_break())
-        {
-            // Avoid complaining about other inference issues for expressions like
-            // `42 >> 1`, where the types are still `{integer}`, but we want to
-            // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
-            // NOTE(eddyb) this was `.cancel()`, but `err`
-            // is borrowed, so we can't fully defuse it.
-            err.downgrade_to_delayed_bug();
-            return;
-        }
-
-        let msg = format!(
-            "multiple `impl`s{} satisfying `{}` found",
-            if has_param_env { " or `where` clauses" } else { "" },
-            predicate
-        );
-        let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
-            format!(":\n{}", post.iter().map(|p| format!("- {p}")).collect::<Vec<_>>().join("\n"),)
-        } else if post.len() == 1 {
-            format!(": `{}`", post[0])
-        } else {
-            String::new()
-        };
-
-        match (spans.len(), crates.len(), crate_names.len()) {
-            (0, 0, 0) => {
-                err.note(format!("cannot satisfy `{predicate}`"));
-            }
-            (0, _, 1) => {
-                err.note(format!("{} in the `{}` crate{}", msg, crates[0], post,));
-            }
-            (0, _, _) => {
-                err.note(format!(
-                    "{} in the following crates: {}{}",
-                    msg,
-                    crate_names.join(", "),
-                    post,
-                ));
-            }
-            (_, 0, 0) => {
-                let span: MultiSpan = spans.into();
-                err.span_note(span, msg);
-            }
-            (_, 1, 1) => {
-                let span: MultiSpan = spans.into();
-                err.span_note(span, msg);
-                err.note(format!("and another `impl` found in the `{}` crate{}", crates[0], post,));
-            }
-            _ => {
-                let span: MultiSpan = spans.into();
-                err.span_note(span, msg);
-                err.note(format!(
-                    "and more `impl`s found in the following crates: {}{}",
-                    crate_names.join(", "),
-                    post,
-                ));
-            }
-        }
-    }
-
     /// Returns `true` if the trait predicate may apply for *some* assignment
     /// to the type parameters.
     fn predicate_can_apply(
@@ -2884,7 +2038,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             self.suggest_unsized_bound_if_applicable(err, obligation);
             if let Some(span) = err.span.primary_span()
                 && let Some(mut diag) =
-                    self.tcx.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion)
+                    self.dcx().steal_non_err(span, StashKey::AssociatedTypeSuggestion)
                 && let Ok(ref mut s1) = err.suggestions
                 && let Ok(ref mut s2) = diag.suggestions
             {
@@ -2894,136 +2048,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    #[instrument(level = "debug", skip_all)]
-    fn suggest_unsized_bound_if_applicable(
-        &self,
-        err: &mut Diag<'_>,
-        obligation: &PredicateObligation<'tcx>,
-    ) {
-        let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
-            obligation.predicate.kind().skip_binder()
-        else {
-            return;
-        };
-        let (ObligationCauseCode::WhereClause(item_def_id, span)
-        | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
-            *obligation.cause.code().peel_derives()
-        else {
-            return;
-        };
-        if span.is_dummy() {
-            return;
-        }
-        debug!(?pred, ?item_def_id, ?span);
-
-        let (Some(node), true) = (
-            self.tcx.hir().get_if_local(item_def_id),
-            self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
-        ) else {
-            return;
-        };
-        self.maybe_suggest_unsized_generics(err, span, node);
-    }
-
-    #[instrument(level = "debug", skip_all)]
-    fn maybe_suggest_unsized_generics(&self, err: &mut Diag<'_>, span: Span, node: Node<'tcx>) {
-        let Some(generics) = node.generics() else {
-            return;
-        };
-        let sized_trait = self.tcx.lang_items().sized_trait();
-        debug!(?generics.params);
-        debug!(?generics.predicates);
-        let Some(param) = generics.params.iter().find(|param| param.span == span) else {
-            return;
-        };
-        // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
-        // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
-        let explicitly_sized = generics
-            .bounds_for_param(param.def_id)
-            .flat_map(|bp| bp.bounds)
-            .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
-        if explicitly_sized {
-            return;
-        }
-        debug!(?param);
-        match node {
-            hir::Node::Item(
-                item @ hir::Item {
-                    // Only suggest indirection for uses of type parameters in ADTs.
-                    kind:
-                        hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
-                    ..
-                },
-            ) => {
-                if self.maybe_indirection_for_unsized(err, item, param) {
-                    return;
-                }
-            }
-            _ => {}
-        };
-
-        // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
-        let (span, separator, open_paren_sp) =
-            if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
-                (s, " +", open_paren_sp)
-            } else {
-                (param.name.ident().span.shrink_to_hi(), ":", None)
-            };
-
-        let mut suggs = vec![];
-        let suggestion = format!("{separator} ?Sized");
-
-        if let Some(open_paren_sp) = open_paren_sp {
-            suggs.push((open_paren_sp, "(".to_string()));
-            suggs.push((span, format!("){suggestion}")));
-        } else {
-            suggs.push((span, suggestion));
-        }
-
-        err.multipart_suggestion_verbose(
-            "consider relaxing the implicit `Sized` restriction",
-            suggs,
-            Applicability::MachineApplicable,
-        );
-    }
-
-    fn maybe_indirection_for_unsized(
-        &self,
-        err: &mut Diag<'_>,
-        item: &Item<'tcx>,
-        param: &GenericParam<'tcx>,
-    ) -> bool {
-        // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
-        // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
-        // is not. Look for invalid "bare" parameter uses, and suggest using indirection.
-        let mut visitor =
-            FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
-        visitor.visit_item(item);
-        if visitor.invalid_spans.is_empty() {
-            return false;
-        }
-        let mut multispan: MultiSpan = param.span.into();
-        multispan.push_span_label(
-            param.span,
-            format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
-        );
-        for sp in visitor.invalid_spans {
-            multispan.push_span_label(
-                sp,
-                format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
-            );
-        }
-        err.span_help(
-            multispan,
-            format!(
-                "you could relax the implicit `Sized` bound on `{T}` if it were \
-                used through indirection like `&{T}` or `Box<{T}>`",
-                T = param.name.ident(),
-            ),
-        );
-        true
-    }
-
     fn is_recursive_obligation(
         &self,
         obligated_types: &mut Vec<Ty<'tcx>>,
@@ -3379,7 +2403,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         found_kind: ty::ClosureKind,
         kind: ty::ClosureKind,
         trait_prefix: &'static str,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let closure_span = self.tcx.def_span(closure_def_id);
 
         let mut err = ClosureKindMismatch {
@@ -3422,7 +2446,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         found_trait_ref: ty::TraitRef<'tcx>,
         expected_trait_ref: ty::TraitRef<'tcx>,
         terr: TypeError<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         let self_ty = found_trait_ref.self_ty();
         let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
             (
@@ -3473,7 +2497,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         span: Span,
         found_trait_ref: ty::TraitRef<'tcx>,
         expected_trait_ref: ty::TraitRef<'tcx>,
-    ) -> Result<Diag<'tcx>, ErrorGuaranteed> {
+    ) -> Result<Diag<'a>, ErrorGuaranteed> {
         let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
         let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
 
@@ -3553,7 +2577,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     })
                     .unwrap_or((found_span, None, found));
 
-                self.report_arg_count_mismatch(
+                self.infcx.report_arg_count_mismatch(
                     span,
                     closure_span,
                     expected,
@@ -3569,7 +2593,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         span: Span,
-    ) -> Result<Diag<'tcx>, ErrorGuaranteed> {
+    ) -> Result<Diag<'a>, ErrorGuaranteed> {
         if !self.tcx.features().generic_const_exprs {
             let guar = self
                 .dcx()
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs
index 4b5b1d77b30..e8d7e80ac56 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/infer_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/infer_ctxt_ext.rs
@@ -1,3 +1,5 @@
+// FIXME(error_reporting): This should be made into private methods on `TypeErrCtxt`.
+
 use crate::infer::InferCtxt;
 use crate::traits::{Obligation, ObligationCause, ObligationCtxt};
 use rustc_errors::{codes::*, pluralize, struct_span_code_err, Applicability, Diag};
@@ -9,8 +11,6 @@ use rustc_span::{Span, DUMMY_SP};
 
 use super::ArgKind;
 
-pub use rustc_infer::traits::error_reporting::*;
-
 #[extension(pub trait InferCtxtExt<'tcx>)]
 impl<'tcx> InferCtxt<'tcx> {
     /// Given some node representing a fn-like thing in the HIR map,
@@ -88,7 +88,7 @@ impl<'tcx> InferCtxt<'tcx> {
         found_args: Vec<ArgKind>,
         is_closure: bool,
         closure_arg_span: Option<Span>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'_> {
         let kind = if is_closure { "closure" } else { "function" };
 
         let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
new file mode 100644
index 00000000000..2131e236401
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/mod.rs
@@ -0,0 +1,385 @@
+pub mod ambiguity;
+mod fulfillment_errors;
+mod infer_ctxt_ext;
+pub mod on_unimplemented;
+mod overflow;
+pub mod suggestions;
+
+use std::iter;
+
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
+use rustc_hir::def_id::DefId;
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{self as hir, LangItem};
+use rustc_infer::infer::error_reporting::TypeErrCtxt;
+use rustc_infer::traits::{
+    Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionError,
+};
+use rustc_macros::extension;
+use rustc_middle::ty::print::PrintTraitRefExt as _;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::{ErrorGuaranteed, ExpnKind, Span};
+
+use ambiguity::TypeErrCtxtAmbiguityExt as _;
+use fulfillment_errors::TypeErrCtxtExt as _;
+use suggestions::TypeErrCtxtExt as _;
+
+use crate::traits::{FulfillmentError, FulfillmentErrorCode};
+
+pub use self::fulfillment_errors::*;
+pub use self::infer_ctxt_ext::*;
+pub use self::overflow::*;
+
+// When outputting impl candidates, prefer showing those that are more similar.
+//
+// We also compare candidates after skipping lifetimes, which has a lower
+// priority than exact matches.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CandidateSimilarity {
+    Exact { ignoring_lifetimes: bool },
+    Fuzzy { ignoring_lifetimes: bool },
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct ImplCandidate<'tcx> {
+    pub trait_ref: ty::TraitRef<'tcx>,
+    pub similarity: CandidateSimilarity,
+    impl_def_id: DefId,
+}
+
+enum GetSafeTransmuteErrorAndReason {
+    Silent,
+    Error { err_msg: String, safe_transmute_explanation: Option<String> },
+}
+
+struct UnsatisfiedConst(pub bool);
+
+/// Crude way of getting back an `Expr` from a `Span`.
+pub struct FindExprBySpan<'hir> {
+    pub span: Span,
+    pub result: Option<&'hir hir::Expr<'hir>>,
+    pub ty_result: Option<&'hir hir::Ty<'hir>>,
+    pub include_closures: bool,
+    pub tcx: TyCtxt<'hir>,
+}
+
+impl<'hir> FindExprBySpan<'hir> {
+    pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
+        Self { span, result: None, ty_result: None, tcx, include_closures: false }
+    }
+}
+
+impl<'v> Visitor<'v> for FindExprBySpan<'v> {
+    type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
+
+    fn nested_visit_map(&mut self) -> Self::Map {
+        self.tcx.hir()
+    }
+
+    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        if self.span == ex.span {
+            self.result = Some(ex);
+        } else {
+            if let hir::ExprKind::Closure(..) = ex.kind
+                && self.include_closures
+                && let closure_header_sp = self.span.with_hi(ex.span.hi())
+                && closure_header_sp == ex.span
+            {
+                self.result = Some(ex);
+            }
+            hir::intravisit::walk_expr(self, ex);
+        }
+    }
+
+    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+        if self.span == ty.span {
+            self.ty_result = Some(ty);
+        } else {
+            hir::intravisit::walk_ty(self, ty);
+        }
+    }
+}
+
+/// Summarizes information
+#[derive(Clone)]
+pub enum ArgKind {
+    /// An argument of non-tuple type. Parameters are (name, ty)
+    Arg(String, String),
+
+    /// An argument of tuple type. For a "found" argument, the span is
+    /// the location in the source of the pattern. For an "expected"
+    /// argument, it will be None. The vector is a list of (name, ty)
+    /// strings for the components of the tuple.
+    Tuple(Option<Span>, Vec<(String, String)>),
+}
+
+impl ArgKind {
+    fn empty() -> ArgKind {
+        ArgKind::Arg("_".to_owned(), "_".to_owned())
+    }
+
+    /// Creates an `ArgKind` from the expected type of an
+    /// argument. It has no name (`_`) and an optional source span.
+    pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
+        match t.kind() {
+            ty::Tuple(tys) => ArgKind::Tuple(
+                span,
+                tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
+            ),
+            _ => ArgKind::Arg("_".to_owned(), t.to_string()),
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+pub enum DefIdOrName {
+    DefId(DefId),
+    Name(&'static str),
+}
+
+#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    fn report_fulfillment_errors(
+        &self,
+        mut errors: Vec<FulfillmentError<'tcx>>,
+    ) -> ErrorGuaranteed {
+        self.sub_relations
+            .borrow_mut()
+            .add_constraints(self, errors.iter().map(|e| e.obligation.predicate));
+
+        #[derive(Debug)]
+        struct ErrorDescriptor<'tcx> {
+            predicate: ty::Predicate<'tcx>,
+            index: Option<usize>, // None if this is an old error
+        }
+
+        let mut error_map: FxIndexMap<_, Vec<_>> = self
+            .reported_trait_errors
+            .borrow()
+            .iter()
+            .map(|(&span, predicates)| {
+                (
+                    span,
+                    predicates
+                        .0
+                        .iter()
+                        .map(|&predicate| ErrorDescriptor { predicate, index: None })
+                        .collect(),
+                )
+            })
+            .collect();
+
+        // Ensure `T: Sized` and `T: WF` obligations come last. This lets us display diagnostics
+        // with more relevant type information and hide redundant E0282 errors.
+        errors.sort_by_key(|e| match e.obligation.predicate.kind().skip_binder() {
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred))
+                if self.tcx.is_lang_item(pred.def_id(), LangItem::Sized) =>
+            {
+                1
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => 3,
+            ty::PredicateKind::Coerce(_) => 2,
+            _ => 0,
+        });
+
+        for (index, error) in errors.iter().enumerate() {
+            // We want to ignore desugarings here: spans are equivalent even
+            // if one is the result of a desugaring and the other is not.
+            let mut span = error.obligation.cause.span;
+            let expn_data = span.ctxt().outer_expn_data();
+            if let ExpnKind::Desugaring(_) = expn_data.kind {
+                span = expn_data.call_site;
+            }
+
+            error_map.entry(span).or_default().push(ErrorDescriptor {
+                predicate: error.obligation.predicate,
+                index: Some(index),
+            });
+        }
+
+        // We do this in 2 passes because we want to display errors in order, though
+        // maybe it *is* better to sort errors by span or something.
+        let mut is_suppressed = vec![false; errors.len()];
+        for (_, error_set) in error_map.iter() {
+            // We want to suppress "duplicate" errors with the same span.
+            for error in error_set {
+                if let Some(index) = error.index {
+                    // Suppress errors that are either:
+                    // 1) strictly implied by another error.
+                    // 2) implied by an error with a smaller index.
+                    for error2 in error_set {
+                        if error2.index.is_some_and(|index2| is_suppressed[index2]) {
+                            // Avoid errors being suppressed by already-suppressed
+                            // errors, to prevent all errors from being suppressed
+                            // at once.
+                            continue;
+                        }
+
+                        if self.error_implies(error2.predicate, error.predicate)
+                            && !(error2.index >= error.index
+                                && self.error_implies(error.predicate, error2.predicate))
+                        {
+                            info!("skipping {:?} (implied by {:?})", error, error2);
+                            is_suppressed[index] = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        let mut reported = None;
+
+        for from_expansion in [false, true] {
+            for (error, suppressed) in iter::zip(&errors, &is_suppressed) {
+                if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
+                    let guar = self.report_fulfillment_error(error);
+                    self.infcx.set_tainted_by_errors(guar);
+                    reported = Some(guar);
+                    // We want to ignore desugarings here: spans are equivalent even
+                    // if one is the result of a desugaring and the other is not.
+                    let mut span = error.obligation.cause.span;
+                    let expn_data = span.ctxt().outer_expn_data();
+                    if let ExpnKind::Desugaring(_) = expn_data.kind {
+                        span = expn_data.call_site;
+                    }
+                    self.reported_trait_errors
+                        .borrow_mut()
+                        .entry(span)
+                        .or_insert_with(|| (vec![], guar))
+                        .0
+                        .push(error.obligation.predicate);
+                }
+            }
+        }
+
+        // It could be that we don't report an error because we have seen an `ErrorReported` from
+        // another source. We should probably be able to fix most of these, but some are delayed
+        // bugs that get a proper error after this function.
+        reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn report_fulfillment_error(&self, error: &FulfillmentError<'tcx>) -> ErrorGuaranteed {
+        let mut error = FulfillmentError {
+            obligation: error.obligation.clone(),
+            code: error.code.clone(),
+            root_obligation: error.root_obligation.clone(),
+        };
+        if matches!(
+            error.code,
+            FulfillmentErrorCode::Select(crate::traits::SelectionError::Unimplemented)
+                | FulfillmentErrorCode::Project(_)
+        ) && self.apply_do_not_recommend(&mut error.obligation)
+        {
+            error.code = FulfillmentErrorCode::Select(SelectionError::Unimplemented);
+        }
+
+        match error.code {
+            FulfillmentErrorCode::Select(ref selection_error) => self.report_selection_error(
+                error.obligation.clone(),
+                &error.root_obligation,
+                selection_error,
+            ),
+            FulfillmentErrorCode::Project(ref e) => {
+                self.report_projection_error(&error.obligation, e)
+            }
+            FulfillmentErrorCode::Ambiguity { overflow: None } => {
+                self.maybe_report_ambiguity(&error.obligation)
+            }
+            FulfillmentErrorCode::Ambiguity { overflow: Some(suggest_increasing_limit) } => {
+                self.report_overflow_no_abort(error.obligation.clone(), suggest_increasing_limit)
+            }
+            FulfillmentErrorCode::Subtype(ref expected_found, ref err) => self
+                .report_mismatched_types(
+                    &error.obligation.cause,
+                    expected_found.expected,
+                    expected_found.found,
+                    *err,
+                )
+                .emit(),
+            FulfillmentErrorCode::ConstEquate(ref expected_found, ref err) => {
+                let mut diag = self.report_mismatched_consts(
+                    &error.obligation.cause,
+                    expected_found.expected,
+                    expected_found.found,
+                    *err,
+                );
+                let code = error.obligation.cause.code().peel_derives().peel_match_impls();
+                if let ObligationCauseCode::WhereClause(..)
+                | ObligationCauseCode::WhereClauseInExpr(..) = code
+                {
+                    self.note_obligation_cause_code(
+                        error.obligation.cause.body_id,
+                        &mut diag,
+                        error.obligation.predicate,
+                        error.obligation.param_env,
+                        code,
+                        &mut vec![],
+                        &mut Default::default(),
+                    );
+                }
+                diag.emit()
+            }
+            FulfillmentErrorCode::Cycle(ref cycle) => self.report_overflow_obligation_cycle(cycle),
+        }
+    }
+}
+
+/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
+/// string.
+pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
+    use std::fmt::Write;
+
+    let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
+    let mut w = "impl".to_owned();
+
+    let args = ty::GenericArgs::identity_for_item(tcx, impl_def_id);
+
+    // FIXME: Currently only handles ?Sized.
+    //        Needs to support ?Move and ?DynSized when they are implemented.
+    let mut types_without_default_bounds = FxIndexSet::default();
+    let sized_trait = tcx.lang_items().sized_trait();
+
+    let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
+    if !arg_names.is_empty() {
+        types_without_default_bounds.extend(args.types());
+        w.push('<');
+        w.push_str(&arg_names.join(", "));
+        w.push('>');
+    }
+
+    write!(
+        w,
+        " {} for {}",
+        trait_ref.print_only_trait_path(),
+        tcx.type_of(impl_def_id).instantiate_identity()
+    )
+    .unwrap();
+
+    // The predicates will contain default bounds like `T: Sized`. We need to
+    // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
+    let predicates = tcx.predicates_of(impl_def_id).predicates;
+    let mut pretty_predicates =
+        Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
+
+    for (p, _) in predicates {
+        if let Some(poly_trait_ref) = p.as_trait_clause() {
+            if Some(poly_trait_ref.def_id()) == sized_trait {
+                // FIXME(#120456) - is `swap_remove` correct?
+                types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
+                continue;
+            }
+        }
+        pretty_predicates.push(p.to_string());
+    }
+
+    pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
+
+    if !pretty_predicates.is_empty() {
+        write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
+    }
+
+    w.push(';');
+    Some(w)
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
index 32c8a454b40..e90fe8fb94d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs
@@ -1,5 +1,10 @@
 use super::{ObligationCauseCode, PredicateObligation};
+use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
+use crate::errors::{
+    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
+};
 use crate::infer::error_reporting::TypeErrCtxt;
+use crate::infer::InferCtxtExt;
 use rustc_ast::AttrArgs;
 use rustc_ast::AttrArgsEq;
 use rustc_ast::AttrKind;
@@ -21,12 +26,6 @@ use rustc_span::Span;
 use std::iter;
 use std::path::PathBuf;
 
-use crate::errors::{
-    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
-};
-
-use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
-
 /// The symbols which are always allowed in a format string
 static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
     kw::SelfUpper,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
new file mode 100644
index 00000000000..061a5a4be20
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/overflow.rs
@@ -0,0 +1,197 @@
+use std::fmt;
+
+use rustc_errors::{
+    struct_span_code_err, Diag, EmissionGuarantee, ErrorGuaranteed, FatalError, E0275,
+};
+use rustc_hir::def::Namespace;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_infer::infer::error_reporting::TypeErrCtxt;
+use rustc_infer::traits::{Obligation, PredicateObligation};
+use rustc_macros::extension;
+use rustc_middle::ty::print::{FmtPrinter, Print};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::Limit;
+use rustc_span::Span;
+use rustc_type_ir::Upcast;
+
+use super::InferCtxtPrivExt;
+use crate::error_reporting::traits::suggestions::TypeErrCtxtExt;
+
+pub enum OverflowCause<'tcx> {
+    DeeplyNormalize(ty::AliasTerm<'tcx>),
+    TraitSolver(ty::Predicate<'tcx>),
+}
+
+pub fn suggest_new_overflow_limit<'tcx, G: EmissionGuarantee>(
+    tcx: TyCtxt<'tcx>,
+    err: &mut Diag<'_, G>,
+) {
+    let suggested_limit = match tcx.recursion_limit() {
+        Limit(0) => Limit(2),
+        limit => limit * 2,
+    };
+    err.help(format!(
+        "consider increasing the recursion limit by adding a \
+         `#![recursion_limit = \"{}\"]` attribute to your crate (`{}`)",
+        suggested_limit,
+        tcx.crate_name(LOCAL_CRATE),
+    ));
+}
+
+#[extension(pub trait TypeErrCtxtOverflowExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
+    /// Reports that an overflow has occurred and halts compilation. We
+    /// halt compilation unconditionally because it is important that
+    /// overflows never be masked -- they basically represent computations
+    /// whose result could not be truly determined and thus we can't say
+    /// if the program type checks or not -- and they are unusual
+    /// occurrences in any case.
+    fn report_overflow_error(
+        &self,
+        cause: OverflowCause<'tcx>,
+        span: Span,
+        suggest_increasing_limit: bool,
+        mutate: impl FnOnce(&mut Diag<'_>),
+    ) -> ! {
+        let mut err = self.build_overflow_error(cause, span, suggest_increasing_limit);
+        mutate(&mut err);
+        err.emit();
+        FatalError.raise();
+    }
+
+    fn build_overflow_error(
+        &self,
+        cause: OverflowCause<'tcx>,
+        span: Span,
+        suggest_increasing_limit: bool,
+    ) -> Diag<'a> {
+        fn with_short_path<'tcx, T>(tcx: TyCtxt<'tcx>, value: T) -> String
+        where
+            T: fmt::Display + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
+        {
+            let s = value.to_string();
+            if s.len() > 50 {
+                // We don't need to save the type to a file, we will be talking about this type already
+                // in a separate note when we explain the obligation, so it will be available that way.
+                let mut cx: FmtPrinter<'_, '_> =
+                    FmtPrinter::new_with_limit(tcx, Namespace::TypeNS, rustc_session::Limit(6));
+                value.print(&mut cx).unwrap();
+                cx.into_buffer()
+            } else {
+                s
+            }
+        }
+
+        let mut err = match cause {
+            OverflowCause::DeeplyNormalize(alias_term) => {
+                let alias_term = self.resolve_vars_if_possible(alias_term);
+                let kind = alias_term.kind(self.tcx).descr();
+                let alias_str = with_short_path(self.tcx, alias_term);
+                struct_span_code_err!(
+                    self.dcx(),
+                    span,
+                    E0275,
+                    "overflow normalizing the {kind} `{alias_str}`",
+                )
+            }
+            OverflowCause::TraitSolver(predicate) => {
+                let predicate = self.resolve_vars_if_possible(predicate);
+                match predicate.kind().skip_binder() {
+                    ty::PredicateKind::Subtype(ty::SubtypePredicate { a, b, a_is_expected: _ })
+                    | ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) => {
+                        struct_span_code_err!(
+                            self.dcx(),
+                            span,
+                            E0275,
+                            "overflow assigning `{a}` to `{b}`",
+                        )
+                    }
+                    _ => {
+                        let pred_str = with_short_path(self.tcx, predicate);
+                        struct_span_code_err!(
+                            self.dcx(),
+                            span,
+                            E0275,
+                            "overflow evaluating the requirement `{pred_str}`",
+                        )
+                    }
+                }
+            }
+        };
+
+        if suggest_increasing_limit {
+            suggest_new_overflow_limit(self.tcx, &mut err);
+        }
+
+        err
+    }
+
+    /// Reports that an overflow has occurred and halts compilation. We
+    /// halt compilation unconditionally because it is important that
+    /// overflows never be masked -- they basically represent computations
+    /// whose result could not be truly determined and thus we can't say
+    /// if the program type checks or not -- and they are unusual
+    /// occurrences in any case.
+    fn report_overflow_obligation<T>(
+        &self,
+        obligation: &Obligation<'tcx, T>,
+        suggest_increasing_limit: bool,
+    ) -> !
+    where
+        T: Upcast<TyCtxt<'tcx>, ty::Predicate<'tcx>> + Clone,
+    {
+        let predicate = obligation.predicate.clone().upcast(self.tcx);
+        let predicate = self.resolve_vars_if_possible(predicate);
+        self.report_overflow_error(
+            OverflowCause::TraitSolver(predicate),
+            obligation.cause.span,
+            suggest_increasing_limit,
+            |err| {
+                self.note_obligation_cause_code(
+                    obligation.cause.body_id,
+                    err,
+                    predicate,
+                    obligation.param_env,
+                    obligation.cause.code(),
+                    &mut vec![],
+                    &mut Default::default(),
+                );
+            },
+        );
+    }
+
+    /// Reports that a cycle was detected which led to overflow and halts
+    /// compilation. This is equivalent to `report_overflow_obligation` except
+    /// that we can give a more helpful error message (and, in particular,
+    /// we do not suggest increasing the overflow limit, which is not
+    /// going to help).
+    fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
+        let cycle = self.resolve_vars_if_possible(cycle.to_owned());
+        assert!(!cycle.is_empty());
+
+        debug!(?cycle, "report_overflow_error_cycle");
+
+        // The 'deepest' obligation is most likely to have a useful
+        // cause 'backtrace'
+        self.report_overflow_obligation(
+            cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
+            false,
+        );
+    }
+
+    fn report_overflow_no_abort(
+        &self,
+        obligation: PredicateObligation<'tcx>,
+        suggest_increasing_limit: bool,
+    ) -> ErrorGuaranteed {
+        let obligation = self.resolve_vars_if_possible(obligation);
+        let mut err = self.build_overflow_error(
+            OverflowCause::TraitSolver(obligation.predicate),
+            obligation.cause.span,
+            suggest_increasing_limit,
+        );
+        self.note_obligation_cause(&mut err, &obligation);
+        self.point_at_returns_when_relevant(&mut err, &obligation);
+        err.emit()
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index f4a026c0367..2cf808f962f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -44,8 +44,8 @@ use std::assert_matches::debug_assert_matches;
 use std::borrow::Cow;
 use std::iter;
 
+use crate::error_reporting::traits::fulfillment_errors::InferCtxtPrivExt;
 use crate::infer::InferCtxtExt as _;
-use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_middle::ty::print::{
     with_forced_trimmed_paths, with_no_trimmed_paths, PrintPolyTraitPredicateExt as _,
@@ -241,8 +241,8 @@ pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
     }
 }
 
-#[extension(pub trait TypeErrCtxtExt<'tcx>)]
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+#[extension(pub trait TypeErrCtxtExt<'a, 'tcx>)]
+impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     fn suggest_restricting_param_bound(
         &self,
         err: &mut Diag<'_>,
@@ -1845,7 +1845,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn point_at_returns_when_relevant(
         &self,
-        err: &mut Diag<'tcx>,
+        err: &mut Diag<'_>,
         obligation: &PredicateObligation<'tcx>,
     ) {
         match obligation.cause.code().peel_derives() {
@@ -1884,7 +1884,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         cause: &ObligationCauseCode<'tcx>,
         found_node: Option<Node<'_>>,
         param_env: ty::ParamEnv<'tcx>,
-    ) -> Diag<'tcx> {
+    ) -> Diag<'a> {
         pub(crate) fn build_fn_sig_ty<'tcx>(
             infcx: &InferCtxt<'tcx>,
             trait_ref: ty::TraitRef<'tcx>,
@@ -2104,7 +2104,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn note_conflicting_closure_bounds(
         &self,
         cause: &ObligationCauseCode<'tcx>,
-        err: &mut Diag<'tcx>,
+        err: &mut Diag<'_>,
     ) {
         // First, look for an `WhereClauseInExpr`, which means we can get
         // the uninstantiated predicate list of the called function. And check
@@ -3472,6 +3472,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     )
                 });
             }
+            // Suppress `compare_type_predicate_entailment` errors for RPITITs, since they
+            // should be implied by the parent method.
+            ObligationCauseCode::CompareImplItem { trait_item_def_id, .. }
+                if tcx.is_impl_trait_in_trait(trait_item_def_id) => {}
             ObligationCauseCode::CompareImplItem { trait_item_def_id, kind, .. } => {
                 let item_name = tcx.item_name(trait_item_def_id);
                 let msg = format!(
@@ -4114,7 +4118,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     expr = binding_expr;
                 }
                 if let hir::Node::Param(param) = parent {
-                    // ...and it is a an fn argument.
+                    // ...and it is an fn argument.
                     let prev_ty = self.resolve_vars_if_possible(
                         typeck_results
                             .node_type_opt(param.hir_id)
@@ -4619,6 +4623,132 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             );
         }
     }
+
+    #[instrument(level = "debug", skip_all)]
+    fn suggest_unsized_bound_if_applicable(
+        &self,
+        err: &mut Diag<'_>,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
+        let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
+            obligation.predicate.kind().skip_binder()
+        else {
+            return;
+        };
+        let (ObligationCauseCode::WhereClause(item_def_id, span)
+        | ObligationCauseCode::WhereClauseInExpr(item_def_id, span, ..)) =
+            *obligation.cause.code().peel_derives()
+        else {
+            return;
+        };
+        if span.is_dummy() {
+            return;
+        }
+        debug!(?pred, ?item_def_id, ?span);
+
+        let (Some(node), true) = (
+            self.tcx.hir().get_if_local(item_def_id),
+            self.tcx.is_lang_item(pred.def_id(), LangItem::Sized),
+        ) else {
+            return;
+        };
+
+        let Some(generics) = node.generics() else {
+            return;
+        };
+        let sized_trait = self.tcx.lang_items().sized_trait();
+        debug!(?generics.params);
+        debug!(?generics.predicates);
+        let Some(param) = generics.params.iter().find(|param| param.span == span) else {
+            return;
+        };
+        // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
+        // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
+        let explicitly_sized = generics
+            .bounds_for_param(param.def_id)
+            .flat_map(|bp| bp.bounds)
+            .any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
+        if explicitly_sized {
+            return;
+        }
+        debug!(?param);
+        match node {
+            hir::Node::Item(
+                item @ hir::Item {
+                    // Only suggest indirection for uses of type parameters in ADTs.
+                    kind:
+                        hir::ItemKind::Enum(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Union(..),
+                    ..
+                },
+            ) => {
+                if self.suggest_indirection_for_unsized(err, item, param) {
+                    return;
+                }
+            }
+            _ => {}
+        };
+
+        // Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
+        let (span, separator, open_paren_sp) =
+            if let Some((s, open_paren_sp)) = generics.bounds_span_for_suggestions(param.def_id) {
+                (s, " +", open_paren_sp)
+            } else {
+                (param.name.ident().span.shrink_to_hi(), ":", None)
+            };
+
+        let mut suggs = vec![];
+        let suggestion = format!("{separator} ?Sized");
+
+        if let Some(open_paren_sp) = open_paren_sp {
+            suggs.push((open_paren_sp, "(".to_string()));
+            suggs.push((span, format!("){suggestion}")));
+        } else {
+            suggs.push((span, suggestion));
+        }
+
+        err.multipart_suggestion_verbose(
+            "consider relaxing the implicit `Sized` restriction",
+            suggs,
+            Applicability::MachineApplicable,
+        );
+    }
+
+    fn suggest_indirection_for_unsized(
+        &self,
+        err: &mut Diag<'_>,
+        item: &hir::Item<'tcx>,
+        param: &hir::GenericParam<'tcx>,
+    ) -> bool {
+        // Suggesting `T: ?Sized` is only valid in an ADT if `T` is only used in a
+        // borrow. `struct S<'a, T: ?Sized>(&'a T);` is valid, `struct S<T: ?Sized>(T);`
+        // is not. Look for invalid "bare" parameter uses, and suggest using indirection.
+        let mut visitor =
+            FindTypeParam { param: param.name.ident().name, invalid_spans: vec![], nested: false };
+        visitor.visit_item(item);
+        if visitor.invalid_spans.is_empty() {
+            return false;
+        }
+        let mut multispan: MultiSpan = param.span.into();
+        multispan.push_span_label(
+            param.span,
+            format!("this could be changed to `{}: ?Sized`...", param.name.ident()),
+        );
+        for sp in visitor.invalid_spans {
+            multispan.push_span_label(
+                sp,
+                format!("...if indirection were used here: `Box<{}>`", param.name.ident()),
+            );
+        }
+        err.span_help(
+            multispan,
+            format!(
+                "you could relax the implicit `Sized` bound on `{T}` if it were \
+                used through indirection like `&{T}` or `Box<{T}>`",
+                T = param.name.ident(),
+            ),
+        );
+        true
+    }
 }
 
 /// Add a hint to add a missing borrow or remove an unnecessary one.
@@ -4995,6 +5125,12 @@ fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
     let ty::ClauseKind::Projection(proj) = clause else {
         return;
     };
+    // avoid ICEing since effects desugared associated types don't have names.
+    // this path should only be hit for `~const` on invalid places, so they
+    // will have an informative error already.
+    if tcx.is_effects_desugared_assoc_ty(proj.projection_term.def_id) {
+        return;
+    }
     let name = tcx.item_name(proj.projection_term.def_id);
     let mut predicates = generics.predicates.iter().peekable();
     let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None;
@@ -5116,3 +5252,46 @@ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) {
 
     (ty, refs)
 }
+
+/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
+/// `param: ?Sized` would be a valid constraint.
+struct FindTypeParam {
+    param: rustc_span::Symbol,
+    invalid_spans: Vec<Span>,
+    nested: bool,
+}
+
+impl<'v> Visitor<'v> for FindTypeParam {
+    fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
+        // Skip where-clauses, to avoid suggesting indirection for type parameters found there.
+    }
+
+    fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
+        // We collect the spans of all uses of the "bare" type param, like in `field: T` or
+        // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
+        // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
+        // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
+        // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
+        // in that case should make what happened clear enough.
+        match ty.kind {
+            hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
+            hir::TyKind::Path(hir::QPath::Resolved(None, path))
+                if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
+            {
+                if !self.nested {
+                    debug!(?ty, "FindTypeParam::visit_ty");
+                    self.invalid_spans.push(ty.span);
+                }
+            }
+            hir::TyKind::Path(_) => {
+                let prev = self.nested;
+                self.nested = true;
+                hir::intravisit::walk_ty(self, ty);
+                self.nested = prev;
+            }
+            _ => {
+                hir::intravisit::walk_ty(self, ty);
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index c95649e2ffb..ad087620ae0 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -1,3 +1,4 @@
+use crate::infer::at::ToTrace;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use crate::traits::{self, Obligation, ObligationCause, ObligationCtxt, SelectionContext};
 
@@ -17,6 +18,16 @@ pub use rustc_infer::infer::*;
 
 #[extension(pub trait InferCtxtExt<'tcx>)]
 impl<'tcx> InferCtxt<'tcx> {
+    fn can_eq<T: ToTrace<'tcx>>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> bool {
+        self.probe(|_| {
+            let ocx = ObligationCtxt::new(self);
+            let Ok(()) = ocx.eq(&ObligationCause::dummy(), param_env, a, b) else {
+                return false;
+            };
+            ocx.select_where_possible().is_empty()
+        })
+    }
+
     fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
         let ty = self.resolve_vars_if_possible(ty);
 
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 50c618bb3bd..d0a12d73941 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -26,12 +26,14 @@
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
 #![feature(type_alias_impl_trait)]
+#![feature(unwrap_infallible)]
 #![recursion_limit = "512"] // For rustdoc
 // tidy-alphabetical-end
 
 #[macro_use]
 extern crate tracing;
 
+pub mod error_reporting;
 pub mod errors;
 pub mod infer;
 pub mod regions;
diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs
index 643d5f80480..f67518a577e 100644
--- a/compiler/rustc_trait_selection/src/solve/delegate.rs
+++ b/compiler/rustc_trait_selection/src/solve/delegate.rs
@@ -1,24 +1,19 @@
 use std::ops::Deref;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::DefId;
 use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
 use rustc_infer::infer::canonical::{
     Canonical, CanonicalExt as _, CanonicalVarInfo, CanonicalVarValues,
 };
-use rustc_infer::infer::{
-    BoundRegionConversionTime, InferCtxt, RegionVariableOrigin, SubregionOrigin, TyCtxtInferExt,
-};
+use rustc_infer::infer::{InferCtxt, RegionVariableOrigin, TyCtxtInferExt};
 use rustc_infer::traits::solve::Goal;
-use rustc_infer::traits::util::supertraits;
 use rustc_infer::traits::{ObligationCause, Reveal};
 use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt as _};
 use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
-use rustc_type_ir::relate::Relate;
 use rustc_type_ir::solve::{Certainty, NoSolution, SolverMode};
 
-use crate::traits::coherence::trait_ref_is_knowable;
 use crate::traits::specialization_graph;
 
 #[repr(transparent)]
@@ -48,13 +43,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
 
     type Span = Span;
 
-    fn solver_mode(&self) -> ty::solve::SolverMode {
-        match self.intercrate {
-            true => SolverMode::Coherence,
-            false => SolverMode::Normal,
-        }
-    }
-
     fn build_with_canonical<V>(
         interner: TyCtxt<'tcx>,
         solver_mode: SolverMode,
@@ -74,104 +62,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         (SolverDelegate(infcx), value, vars)
     }
 
-    fn universe(&self) -> ty::UniverseIndex {
-        self.0.universe()
-    }
-
-    fn create_next_universe(&self) -> ty::UniverseIndex {
-        self.0.create_next_universe()
-    }
-
-    fn universe_of_ty(&self, vid: ty::TyVid) -> Option<ty::UniverseIndex> {
-        // FIXME(BoxyUwU): this is kind of jank and means that printing unresolved
-        // ty infers will give you the universe of the var it resolved to not the universe
-        // it actually had. It also means that if you have a `?0.1` and infer it to `u8` then
-        // try to print out `?0.1` it will just print `?0`.
-        match self.0.probe_ty_var(vid) {
-            Err(universe) => Some(universe),
-            Ok(_) => None,
-        }
-    }
-
-    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex> {
-        match self.0.inner.borrow_mut().unwrap_region_constraints().probe_value(lt) {
-            Err(universe) => Some(universe),
-            Ok(_) => None,
-        }
-    }
-
-    fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex> {
-        // Same issue as with `universe_of_ty`
-        match self.0.probe_const_var(ct) {
-            Err(universe) => Some(universe),
-            Ok(_) => None,
-        }
-    }
-
-    fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid {
-        self.0.root_var(var)
-    }
-
-    fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid {
-        self.0.root_const_var(var)
-    }
-
-    fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> Ty<'tcx> {
-        match self.0.probe_ty_var(vid) {
-            Ok(ty) => ty,
-            Err(_) => Ty::new_var(self.0.tcx, self.0.root_var(vid)),
-        }
-    }
-
-    fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> Ty<'tcx> {
-        self.0.opportunistic_resolve_int_var(vid)
-    }
-
-    fn opportunistic_resolve_float_var(&self, vid: ty::FloatVid) -> Ty<'tcx> {
-        self.0.opportunistic_resolve_float_var(vid)
-    }
-
-    fn opportunistic_resolve_ct_var(&self, vid: ty::ConstVid) -> ty::Const<'tcx> {
-        match self.0.probe_const_var(vid) {
-            Ok(ct) => ct,
-            Err(_) => ty::Const::new_var(self.0.tcx, self.0.root_const_var(vid)),
-        }
-    }
-
-    fn opportunistic_resolve_effect_var(&self, vid: ty::EffectVid) -> ty::Const<'tcx> {
-        match self.0.probe_effect_var(vid) {
-            Some(ct) => ct,
-            None => ty::Const::new_infer(
-                self.0.tcx,
-                ty::InferConst::EffectVar(self.0.root_effect_var(vid)),
-            ),
-        }
-    }
-
-    fn opportunistic_resolve_lt_var(&self, vid: ty::RegionVid) -> ty::Region<'tcx> {
-        self.0
-            .inner
-            .borrow_mut()
-            .unwrap_region_constraints()
-            .opportunistic_resolve_var(self.0.tcx, vid)
-    }
-
-    fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
-        self.0.defining_opaque_types()
-    }
-
-    fn next_ty_infer(&self) -> Ty<'tcx> {
-        self.0.next_ty_var(DUMMY_SP)
-    }
-
-    fn next_const_infer(&self) -> ty::Const<'tcx> {
-        self.0.next_const_var(DUMMY_SP)
-    }
-
-    fn fresh_args_for_item(&self, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
-        self.0.fresh_args_for_item(DUMMY_SP, def_id)
-    }
-
     fn fresh_var_for_kind_with_span(
         &self,
         arg: ty::GenericArg<'tcx>,
@@ -186,68 +76,10 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         }
     }
 
-    fn instantiate_binder_with_infer<T: TypeFoldable<TyCtxt<'tcx>> + Copy>(
-        &self,
-        value: ty::Binder<'tcx, T>,
-    ) -> T {
-        self.0.instantiate_binder_with_fresh_vars(
-            DUMMY_SP,
-            BoundRegionConversionTime::HigherRankedType,
-            value,
-        )
-    }
-
-    fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>> + Copy, U>(
-        &self,
-        value: ty::Binder<'tcx, T>,
-        f: impl FnOnce(T) -> U,
-    ) -> U {
-        self.0.enter_forall(value, f)
-    }
-
-    fn relate<T: Relate<TyCtxt<'tcx>>>(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        variance: ty::Variance,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.0.at(&ObligationCause::dummy(), param_env).relate_no_trace(lhs, variance, rhs)
-    }
-
-    fn eq_structurally_relating_aliases<T: Relate<TyCtxt<'tcx>>>(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: T,
-        rhs: T,
-    ) -> Result<Vec<Goal<'tcx, ty::Predicate<'tcx>>>, NoSolution> {
-        self.0
-            .at(&ObligationCause::dummy(), param_env)
-            .eq_structurally_relating_aliases_no_trace(lhs, rhs)
-    }
-
-    fn resolve_vars_if_possible<T>(&self, value: T) -> T
-    where
-        T: TypeFoldable<TyCtxt<'tcx>>,
-    {
-        self.0.resolve_vars_if_possible(value)
-    }
-
-    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T {
-        self.0.probe(|_| probe())
-    }
-
     fn leak_check(&self, max_input_universe: ty::UniverseIndex) -> Result<(), NoSolution> {
         self.0.leak_check(max_input_universe, None).map_err(|_| NoSolution)
     }
 
-    fn elaborate_supertraits(
-        interner: TyCtxt<'tcx>,
-        trait_ref: ty::Binder<'tcx, ty::TraitRef<'tcx>>,
-    ) -> impl Iterator<Item = ty::Binder<'tcx, ty::TraitRef<'tcx>>> {
-        supertraits(interner, trait_ref)
-    }
-
     fn try_const_eval_resolve(
         &self,
         param_env: ty::ParamEnv<'tcx>,
@@ -265,14 +97,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         }
     }
 
-    fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
-        self.0.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup)
-    }
-
-    fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {
-        self.0.register_region_obligation_with_cause(ty, r, &ObligationCause::dummy());
-    }
-
     fn well_formed_goals(
         &self,
         param_env: ty::ParamEnv<'tcx>,
@@ -367,15 +191,6 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate<
         let _ = self.take_opaque_types();
     }
 
-    fn trait_ref_is_knowable<E: std::fmt::Debug>(
-        &self,
-        trait_ref: ty::TraitRef<'tcx>,
-        lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-    ) -> Result<bool, E> {
-        trait_ref_is_knowable(&self.0, trait_ref, lazily_normalize_ty)
-            .map(|is_knowable| is_knowable.is_ok())
-    }
-
     fn fetch_eligible_assoc_item(
         &self,
         param_env: ty::ParamEnv<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index 2679da942b7..ca313590265 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,7 +1,7 @@
 use std::fmt::Debug;
 use std::marker::PhantomData;
 
-use crate::traits::error_reporting::{OverflowCause, TypeErrCtxtExt};
+use crate::error_reporting::traits::{OverflowCause, TypeErrCtxtOverflowExt};
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
 use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
 use rustc_data_structures::stack::ensure_sufficient_stack;
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index a4177d8a93f..7e996c5c5ef 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -25,41 +25,13 @@ use rustc_middle::traits::specialization_graph::OverlapMode;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
 use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
+pub use rustc_next_trait_solver::coherence::*;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, DUMMY_SP};
 use std::fmt::Debug;
-use std::ops::ControlFlow;
 
-use super::error_reporting::suggest_new_overflow_limit;
 use super::ObligationCtxt;
-
-/// Whether we do the orphan check relative to this crate or to some remote crate.
-#[derive(Copy, Clone, Debug)]
-pub enum InCrate {
-    Local { mode: OrphanCheckMode },
-    Remote,
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum OrphanCheckMode {
-    /// Proper orphan check.
-    Proper,
-    /// Improper orphan check for backward compatibility.
-    ///
-    /// In this mode, type params inside projections are considered to be covered
-    /// even if the projection may normalize to a type that doesn't actually cover
-    /// them. This is unsound. See also [#124559] and [#99554].
-    ///
-    /// [#124559]: https://github.com/rust-lang/rust/issues/124559
-    /// [#99554]: https://github.com/rust-lang/rust/issues/99554
-    Compat,
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum Conflict {
-    Upstream,
-    Downstream,
-}
+use crate::error_reporting::traits::suggest_new_overflow_limit;
 
 pub struct OverlapResult<'tcx> {
     pub impl_header: ty::ImplHeader<'tcx>,
@@ -121,7 +93,7 @@ pub fn overlapping_impls(
     // Before doing expensive operations like entering an inference context, do
     // a quick check via fast_reject to tell if the impl headers could possibly
     // unify.
-    let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsCandidateKey };
+    let drcx = DeepRejectCtxt::new(tcx, TreatParams::AsCandidateKey);
     let impl1_ref = tcx.impl_trait_ref(impl1_def_id);
     let impl2_ref = tcx.impl_trait_ref(impl2_def_id);
     let may_overlap = match (impl1_ref, impl2_ref) {
@@ -612,426 +584,6 @@ fn try_prove_negated_where_clause<'tcx>(
     true
 }
 
-/// Returns whether all impls which would apply to the `trait_ref`
-/// e.g. `Ty: Trait<Arg>` are already known in the local crate.
-///
-/// This both checks whether any downstream or sibling crates could
-/// implement it and whether an upstream crate can add this impl
-/// without breaking backwards compatibility.
-#[instrument(level = "debug", skip(infcx, lazily_normalize_ty), ret)]
-pub fn trait_ref_is_knowable<'tcx, E: Debug>(
-    infcx: &InferCtxt<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-    mut lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-) -> Result<Result<(), Conflict>, E> {
-    if orphan_check_trait_ref(infcx, trait_ref, InCrate::Remote, &mut lazily_normalize_ty)?.is_ok()
-    {
-        // A downstream or cousin crate is allowed to implement some
-        // generic parameters of this trait-ref.
-        return Ok(Err(Conflict::Downstream));
-    }
-
-    if trait_ref_is_local_or_fundamental(infcx.tcx, trait_ref) {
-        // This is a local or fundamental trait, so future-compatibility
-        // is no concern. We know that downstream/cousin crates are not
-        // allowed to implement a generic parameter of this trait ref,
-        // which means impls could only come from dependencies of this
-        // crate, which we already know about.
-        return Ok(Ok(()));
-    }
-
-    // This is a remote non-fundamental trait, so if another crate
-    // can be the "final owner" of the generic parameters of this trait-ref,
-    // they are allowed to implement it future-compatibly.
-    //
-    // However, if we are a final owner, then nobody else can be,
-    // and if we are an intermediate owner, then we don't care
-    // about future-compatibility, which means that we're OK if
-    // we are an owner.
-    if orphan_check_trait_ref(
-        infcx,
-        trait_ref,
-        InCrate::Local { mode: OrphanCheckMode::Proper },
-        &mut lazily_normalize_ty,
-    )?
-    .is_ok()
-    {
-        Ok(Ok(()))
-    } else {
-        Ok(Err(Conflict::Upstream))
-    }
-}
-
-pub fn trait_ref_is_local_or_fundamental<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-) -> bool {
-    trait_ref.def_id.is_local() || tcx.has_attr(trait_ref.def_id, sym::fundamental)
-}
-
-#[derive(Debug, Copy, Clone)]
-pub enum IsFirstInputType {
-    No,
-    Yes,
-}
-
-impl From<bool> for IsFirstInputType {
-    fn from(b: bool) -> IsFirstInputType {
-        match b {
-            false => IsFirstInputType::No,
-            true => IsFirstInputType::Yes,
-        }
-    }
-}
-
-#[derive(Debug)]
-pub enum OrphanCheckErr<'tcx, T> {
-    NonLocalInputType(Vec<(Ty<'tcx>, IsFirstInputType)>),
-    UncoveredTyParams(UncoveredTyParams<'tcx, T>),
-}
-
-#[derive(Debug)]
-pub struct UncoveredTyParams<'tcx, T> {
-    pub uncovered: T,
-    pub local_ty: Option<Ty<'tcx>>,
-}
-
-/// Checks whether a trait-ref is potentially implementable by a crate.
-///
-/// The current rule is that a trait-ref orphan checks in a crate C:
-///
-/// 1. Order the parameters in the trait-ref in generic parameters order
-/// - Self first, others linearly (e.g., `<U as Foo<V, W>>` is U < V < W).
-/// 2. Of these type parameters, there is at least one type parameter
-///    in which, walking the type as a tree, you can reach a type local
-///    to C where all types in-between are fundamental types. Call the
-///    first such parameter the "local key parameter".
-///     - e.g., `Box<LocalType>` is OK, because you can visit LocalType
-///       going through `Box`, which is fundamental.
-///     - similarly, `FundamentalPair<Vec<()>, Box<LocalType>>` is OK for
-///       the same reason.
-///     - but (knowing that `Vec<T>` is non-fundamental, and assuming it's
-///       not local), `Vec<LocalType>` is bad, because `Vec<->` is between
-///       the local type and the type parameter.
-/// 3. Before this local type, no generic type parameter of the impl must
-///    be reachable through fundamental types.
-///     - e.g. `impl<T> Trait<LocalType> for Vec<T>` is fine, as `Vec` is not fundamental.
-///     - while `impl<T> Trait<LocalType> for Box<T>` results in an error, as `T` is
-///       reachable through the fundamental type `Box`.
-/// 4. Every type in the local key parameter not known in C, going
-///    through the parameter's type tree, must appear only as a subtree of
-///    a type local to C, with only fundamental types between the type
-///    local to C and the local key parameter.
-///     - e.g., `Vec<LocalType<T>>>` (or equivalently `Box<Vec<LocalType<T>>>`)
-///     is bad, because the only local type with `T` as a subtree is
-///     `LocalType<T>`, and `Vec<->` is between it and the type parameter.
-///     - similarly, `FundamentalPair<LocalType<T>, T>` is bad, because
-///     the second occurrence of `T` is not a subtree of *any* local type.
-///     - however, `LocalType<Vec<T>>` is OK, because `T` is a subtree of
-///     `LocalType<Vec<T>>`, which is local and has no types between it and
-///     the type parameter.
-///
-/// The orphan rules actually serve several different purposes:
-///
-/// 1. They enable link-safety - i.e., 2 mutually-unknowing crates (where
-///    every type local to one crate is unknown in the other) can't implement
-///    the same trait-ref. This follows because it can be seen that no such
-///    type can orphan-check in 2 such crates.
-///
-///    To check that a local impl follows the orphan rules, we check it in
-///    InCrate::Local mode, using type parameters for the "generic" types.
-///
-///    In InCrate::Local mode the orphan check succeeds if the current crate
-///    is definitely allowed to implement the given trait (no false positives).
-///
-/// 2. They ground negative reasoning for coherence. If a user wants to
-///    write both a conditional blanket impl and a specific impl, we need to
-///    make sure they do not overlap. For example, if we write
-///    ```ignore (illustrative)
-///    impl<T> IntoIterator for Vec<T>
-///    impl<T: Iterator> IntoIterator for T
-///    ```
-///    We need to be able to prove that `Vec<$0>: !Iterator` for every type $0.
-///    We can observe that this holds in the current crate, but we need to make
-///    sure this will also hold in all unknown crates (both "independent" crates,
-///    which we need for link-safety, and also child crates, because we don't want
-///    child crates to get error for impl conflicts in a *dependency*).
-///
-///    For that, we only allow negative reasoning if, for every assignment to the
-///    inference variables, every unknown crate would get an orphan error if they
-///    try to implement this trait-ref. To check for this, we use InCrate::Remote
-///    mode. That is sound because we already know all the impls from known crates.
-///
-///    In InCrate::Remote mode the orphan check succeeds if a foreign crate
-///    *could* implement the given trait (no false negatives).
-///
-/// 3. For non-`#[fundamental]` traits, they guarantee that parent crates can
-///    add "non-blanket" impls without breaking negative reasoning in dependent
-///    crates. This is the "rebalancing coherence" (RFC 1023) restriction.
-///
-///    For that, we only allow a crate to perform negative reasoning on
-///    non-local-non-`#[fundamental]` if there's a local key parameter as per (2).
-///
-///    Because we never perform negative reasoning generically (coherence does
-///    not involve type parameters), this can be interpreted as doing the full
-///    orphan check (using InCrate::Local mode), instantiating non-local known
-///    types for all inference variables.
-///
-///    This allows for crates to future-compatibly add impls as long as they
-///    can't apply to types with a key parameter in a child crate - applying
-///    the rules, this basically means that every type parameter in the impl
-///    must appear behind a non-fundamental type (because this is not a
-///    type-system requirement, crate owners might also go for "semantic
-///    future-compatibility" involving things such as sealed traits, but
-///    the above requirement is sufficient, and is necessary in "open world"
-///    cases).
-///
-/// Note that this function is never called for types that have both type
-/// parameters and inference variables.
-#[instrument(level = "trace", skip(infcx, lazily_normalize_ty), ret)]
-pub fn orphan_check_trait_ref<'tcx, E: Debug>(
-    infcx: &InferCtxt<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-    in_crate: InCrate,
-    lazily_normalize_ty: impl FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-) -> Result<Result<(), OrphanCheckErr<'tcx, Ty<'tcx>>>, E> {
-    if trait_ref.has_param() {
-        bug!("orphan check only expects inference variables: {trait_ref:?}");
-    }
-
-    let mut checker = OrphanChecker::new(infcx, in_crate, lazily_normalize_ty);
-    Ok(match trait_ref.visit_with(&mut checker) {
-        ControlFlow::Continue(()) => Err(OrphanCheckErr::NonLocalInputType(checker.non_local_tys)),
-        ControlFlow::Break(residual) => match residual {
-            OrphanCheckEarlyExit::NormalizationFailure(err) => return Err(err),
-            OrphanCheckEarlyExit::UncoveredTyParam(ty) => {
-                // Does there exist some local type after the `ParamTy`.
-                checker.search_first_local_ty = true;
-                let local_ty = match trait_ref.visit_with(&mut checker).break_value() {
-                    Some(OrphanCheckEarlyExit::LocalTy(local_ty)) => Some(local_ty),
-                    _ => None,
-                };
-                Err(OrphanCheckErr::UncoveredTyParams(UncoveredTyParams {
-                    uncovered: ty,
-                    local_ty,
-                }))
-            }
-            OrphanCheckEarlyExit::LocalTy(_) => Ok(()),
-        },
-    })
-}
-
-struct OrphanChecker<'a, 'tcx, F> {
-    infcx: &'a InferCtxt<'tcx>,
-    in_crate: InCrate,
-    in_self_ty: bool,
-    lazily_normalize_ty: F,
-    /// Ignore orphan check failures and exclusively search for the first local type.
-    search_first_local_ty: bool,
-    non_local_tys: Vec<(Ty<'tcx>, IsFirstInputType)>,
-}
-
-impl<'a, 'tcx, F, E> OrphanChecker<'a, 'tcx, F>
-where
-    F: FnOnce(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-{
-    fn new(infcx: &'a InferCtxt<'tcx>, in_crate: InCrate, lazily_normalize_ty: F) -> Self {
-        OrphanChecker {
-            infcx,
-            in_crate,
-            in_self_ty: true,
-            lazily_normalize_ty,
-            search_first_local_ty: false,
-            non_local_tys: Vec::new(),
-        }
-    }
-
-    fn found_non_local_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
-        self.non_local_tys.push((t, self.in_self_ty.into()));
-        ControlFlow::Continue(())
-    }
-
-    fn found_uncovered_ty_param(
-        &mut self,
-        ty: Ty<'tcx>,
-    ) -> ControlFlow<OrphanCheckEarlyExit<'tcx, E>> {
-        if self.search_first_local_ty {
-            return ControlFlow::Continue(());
-        }
-
-        ControlFlow::Break(OrphanCheckEarlyExit::UncoveredTyParam(ty))
-    }
-
-    fn def_id_is_local(&mut self, def_id: DefId) -> bool {
-        match self.in_crate {
-            InCrate::Local { .. } => def_id.is_local(),
-            InCrate::Remote => false,
-        }
-    }
-}
-
-enum OrphanCheckEarlyExit<'tcx, E> {
-    NormalizationFailure(E),
-    UncoveredTyParam(Ty<'tcx>),
-    LocalTy(Ty<'tcx>),
-}
-
-impl<'a, 'tcx, F, E> TypeVisitor<TyCtxt<'tcx>> for OrphanChecker<'a, 'tcx, F>
-where
-    F: FnMut(Ty<'tcx>) -> Result<Ty<'tcx>, E>,
-{
-    type Result = ControlFlow<OrphanCheckEarlyExit<'tcx, E>>;
-
-    fn visit_region(&mut self, _r: ty::Region<'tcx>) -> Self::Result {
-        ControlFlow::Continue(())
-    }
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-        let ty = self.infcx.shallow_resolve(ty);
-        let ty = match (self.lazily_normalize_ty)(ty) {
-            Ok(norm_ty) if norm_ty.is_ty_var() => ty,
-            Ok(norm_ty) => norm_ty,
-            Err(err) => return ControlFlow::Break(OrphanCheckEarlyExit::NormalizationFailure(err)),
-        };
-
-        let result = match *ty.kind() {
-            ty::Bool
-            | ty::Char
-            | ty::Int(..)
-            | ty::Uint(..)
-            | ty::Float(..)
-            | ty::Str
-            | ty::FnDef(..)
-            | ty::Pat(..)
-            | ty::FnPtr(_)
-            | ty::Array(..)
-            | ty::Slice(..)
-            | ty::RawPtr(..)
-            | ty::Never
-            | ty::Tuple(..) => self.found_non_local_ty(ty),
-
-            ty::Param(..) => bug!("unexpected ty param"),
-
-            ty::Placeholder(..) | ty::Bound(..) | ty::Infer(..) => {
-                match self.in_crate {
-                    InCrate::Local { .. } => self.found_uncovered_ty_param(ty),
-                    // The inference variable might be unified with a local
-                    // type in that remote crate.
-                    InCrate::Remote => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
-                }
-            }
-
-            // A rigid alias may normalize to anything.
-            // * If it references an infer var, placeholder or bound ty, it may
-            //   normalize to that, so we have to treat it as an uncovered ty param.
-            // * Otherwise it may normalize to any non-type-generic type
-            //   be it local or non-local.
-            ty::Alias(kind, _) => {
-                if ty.has_type_flags(
-                    ty::TypeFlags::HAS_TY_PLACEHOLDER
-                        | ty::TypeFlags::HAS_TY_BOUND
-                        | ty::TypeFlags::HAS_TY_INFER,
-                ) {
-                    match self.in_crate {
-                        InCrate::Local { mode } => match kind {
-                            ty::Projection if let OrphanCheckMode::Compat = mode => {
-                                ControlFlow::Continue(())
-                            }
-                            _ => self.found_uncovered_ty_param(ty),
-                        },
-                        InCrate::Remote => {
-                            // The inference variable might be unified with a local
-                            // type in that remote crate.
-                            ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
-                        }
-                    }
-                } else {
-                    // Regarding *opaque types* specifically, we choose to treat them as non-local,
-                    // even those that appear within the same crate. This seems somewhat surprising
-                    // at first, but makes sense when you consider that opaque types are supposed
-                    // to hide the underlying type *within the same crate*. When an opaque type is
-                    // used from outside the module where it is declared, it should be impossible to
-                    // observe anything about it other than the traits that it implements.
-                    //
-                    // The alternative would be to look at the underlying type to determine whether
-                    // or not the opaque type itself should be considered local.
-                    //
-                    // However, this could make it a breaking change to switch the underlying hidden
-                    // type from a local type to a remote type. This would violate the rule that
-                    // opaque types should be completely opaque apart from the traits that they
-                    // implement, so we don't use this behavior.
-                    // Addendum: Moreover, revealing the underlying type is likely to cause cycle
-                    // errors as we rely on coherence / the specialization graph during typeck.
-
-                    self.found_non_local_ty(ty)
-                }
-            }
-
-            // For fundamental types, we just look inside of them.
-            ty::Ref(_, ty, _) => ty.visit_with(self),
-            ty::Adt(def, args) => {
-                if self.def_id_is_local(def.did()) {
-                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
-                } else if def.is_fundamental() {
-                    args.visit_with(self)
-                } else {
-                    self.found_non_local_ty(ty)
-                }
-            }
-            ty::Foreign(def_id) => {
-                if self.def_id_is_local(def_id) {
-                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
-                } else {
-                    self.found_non_local_ty(ty)
-                }
-            }
-            ty::Dynamic(tt, ..) => {
-                let principal = tt.principal().map(|p| p.def_id());
-                if principal.is_some_and(|p| self.def_id_is_local(p)) {
-                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
-                } else {
-                    self.found_non_local_ty(ty)
-                }
-            }
-            ty::Error(_) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
-            ty::Closure(did, ..) | ty::CoroutineClosure(did, ..) | ty::Coroutine(did, ..) => {
-                if self.def_id_is_local(did) {
-                    ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty))
-                } else {
-                    self.found_non_local_ty(ty)
-                }
-            }
-            // This should only be created when checking whether we have to check whether some
-            // auto trait impl applies. There will never be multiple impls, so we can just
-            // act as if it were a local type here.
-            ty::CoroutineWitness(..) => ControlFlow::Break(OrphanCheckEarlyExit::LocalTy(ty)),
-        };
-        // A bit of a hack, the `OrphanChecker` is only used to visit a `TraitRef`, so
-        // the first type we visit is always the self type.
-        self.in_self_ty = false;
-        result
-    }
-
-    /// All possible values for a constant parameter already exist
-    /// in the crate defining the trait, so they are always non-local[^1].
-    ///
-    /// Because there's no way to have an impl where the first local
-    /// generic argument is a constant, we also don't have to fail
-    /// the orphan check when encountering a parameter or a generic constant.
-    ///
-    /// This means that we can completely ignore constants during the orphan check.
-    ///
-    /// See `tests/ui/coherence/const-generics-orphan-check-ok.rs` for examples.
-    ///
-    /// [^1]: This might not hold for function pointers or trait objects in the future.
-    /// As these should be quite rare as const arguments and especially rare as impl
-    /// parameters, allowing uncovered const parameters in impls seems more useful
-    /// than allowing `impl<T> Trait<local_fn_ptr, T> for i32` to compile.
-    fn visit_const(&mut self, _c: ty::Const<'tcx>) -> Self::Result {
-        ControlFlow::Continue(())
-    }
-}
-
 /// Compute the `intercrate_ambiguity_causes` for the new solver using
 /// "proof trees".
 ///
diff --git a/compiler/rustc_trait_selection/src/traits/engine.rs b/compiler/rustc_trait_selection/src/traits/engine.rs
index 811f61d2bf3..bdc27e734f9 100644
--- a/compiler/rustc_trait_selection/src/traits/engine.rs
+++ b/compiler/rustc_trait_selection/src/traits/engine.rs
@@ -3,10 +3,10 @@ use std::fmt::Debug;
 
 use super::{FromSolverError, TraitEngine};
 use super::{FulfillmentContext, ScrubbedTraitError};
+use crate::error_reporting::traits::TypeErrCtxtExt;
 use crate::regions::InferCtxtRegionExt;
 use crate::solve::FulfillmentCtxt as NextFulfillmentCtxt;
 use crate::solve::NextSolverError;
-use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::fulfill::OldSolverError;
 use crate::traits::NormalizeExt;
 use crate::traits::StructurallyNormalizeExt;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
deleted file mode 100644
index e6cb28df593..00000000000
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{BoundRegionConversionTime, InferCtxt};
-use rustc_infer::traits::util::elaborate;
-use rustc_infer::traits::{Obligation, ObligationCause, PolyTraitObligation};
-use rustc_middle::ty;
-use rustc_span::{Span, DUMMY_SP};
-
-use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::ObligationCtxt;
-
-#[derive(Debug)]
-pub enum CandidateSource {
-    DefId(DefId),
-    ParamEnv(Span),
-}
-
-pub fn compute_applicable_impls_for_diagnostics<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    obligation: &PolyTraitObligation<'tcx>,
-) -> Vec<CandidateSource> {
-    let tcx = infcx.tcx;
-    let param_env = obligation.param_env;
-
-    let predicate_polarity = obligation.predicate.skip_binder().polarity;
-
-    let impl_may_apply = |impl_def_id| {
-        let ocx = ObligationCtxt::new(infcx);
-        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
-            let obligation_trait_ref = ocx.normalize(
-                &ObligationCause::dummy(),
-                param_env,
-                placeholder_obligation.trait_ref,
-            );
-
-            let impl_args = infcx.fresh_args_for_item(DUMMY_SP, impl_def_id);
-            let impl_trait_ref =
-                tcx.impl_trait_ref(impl_def_id).unwrap().instantiate(tcx, impl_args);
-            let impl_trait_ref =
-                ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
-
-            if let Err(_) =
-                ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
-            {
-                return false;
-            }
-
-            let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
-            let impl_polarity = impl_trait_header.polarity;
-
-            match (impl_polarity, predicate_polarity) {
-                (ty::ImplPolarity::Positive, ty::PredicatePolarity::Positive)
-                | (ty::ImplPolarity::Negative, ty::PredicatePolarity::Negative) => {}
-                _ => return false,
-            }
-
-            let obligations = tcx
-                .predicates_of(impl_def_id)
-                .instantiate(tcx, impl_args)
-                .into_iter()
-                .map(|(predicate, _)| {
-                    Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
-                })
-                // Kinda hacky, but let's just throw away obligations that overflow.
-                // This may reduce the accuracy of this check (if the obligation guides
-                // inference or it actually resulted in error after others are processed)
-                // ... but this is diagnostics code.
-                .filter(|obligation| {
-                    infcx.next_trait_solver() || infcx.evaluate_obligation(obligation).is_ok()
-                });
-            ocx.register_obligations(obligations);
-
-            ocx.select_where_possible().is_empty()
-        })
-    };
-
-    let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
-        let ocx = ObligationCtxt::new(infcx);
-        infcx.enter_forall(obligation.predicate, |placeholder_obligation| {
-            let obligation_trait_ref = ocx.normalize(
-                &ObligationCause::dummy(),
-                param_env,
-                placeholder_obligation.trait_ref,
-            );
-
-            let param_env_predicate = infcx.instantiate_binder_with_fresh_vars(
-                DUMMY_SP,
-                BoundRegionConversionTime::HigherRankedType,
-                poly_trait_predicate,
-            );
-            let param_env_trait_ref =
-                ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
-
-            if let Err(_) = ocx.eq(
-                &ObligationCause::dummy(),
-                param_env,
-                obligation_trait_ref,
-                param_env_trait_ref,
-            ) {
-                return false;
-            }
-
-            ocx.select_where_possible().is_empty()
-        })
-    };
-
-    let mut ambiguities = Vec::new();
-
-    tcx.for_each_relevant_impl(
-        obligation.predicate.def_id(),
-        obligation.predicate.skip_binder().trait_ref.self_ty(),
-        |impl_def_id| {
-            if infcx.probe(|_| impl_may_apply(impl_def_id)) {
-                ambiguities.push(CandidateSource::DefId(impl_def_id))
-            }
-        },
-    );
-
-    let predicates =
-        tcx.predicates_of(obligation.cause.body_id.to_def_id()).instantiate_identity(tcx);
-    for (pred, span) in elaborate(tcx, predicates.into_iter()) {
-        let kind = pred.kind();
-        if let ty::ClauseKind::Trait(trait_pred) = kind.skip_binder()
-            && param_env_candidate_may_apply(kind.rebind(trait_pred))
-        {
-            if kind.rebind(trait_pred.trait_ref)
-                == ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_pred.def_id()))
-            {
-                ambiguities.push(CandidateSource::ParamEnv(tcx.def_span(trait_pred.def_id())))
-            } else {
-                ambiguities.push(CandidateSource::ParamEnv(span))
-            }
-        }
-    }
-
-    ambiguities
-}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
deleted file mode 100644
index 633d488f7be..00000000000
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ /dev/null
@@ -1,182 +0,0 @@
-// ignore-tidy-filelength :(
-
-pub mod ambiguity;
-mod infer_ctxt_ext;
-pub mod on_unimplemented;
-pub mod suggestions;
-mod type_err_ctxt_ext;
-
-use super::{Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
-use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::Visitor;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_span::Span;
-use std::ops::ControlFlow;
-
-pub use self::infer_ctxt_ext::*;
-pub use self::type_err_ctxt_ext::*;
-
-// When outputting impl candidates, prefer showing those that are more similar.
-//
-// We also compare candidates after skipping lifetimes, which has a lower
-// priority than exact matches.
-#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
-pub enum CandidateSimilarity {
-    Exact { ignoring_lifetimes: bool },
-    Fuzzy { ignoring_lifetimes: bool },
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub struct ImplCandidate<'tcx> {
-    pub trait_ref: ty::TraitRef<'tcx>,
-    pub similarity: CandidateSimilarity,
-    impl_def_id: DefId,
-}
-
-enum GetSafeTransmuteErrorAndReason {
-    Silent,
-    Error { err_msg: String, safe_transmute_explanation: Option<String> },
-}
-
-struct UnsatisfiedConst(pub bool);
-
-/// Crude way of getting back an `Expr` from a `Span`.
-pub struct FindExprBySpan<'hir> {
-    pub span: Span,
-    pub result: Option<&'hir hir::Expr<'hir>>,
-    pub ty_result: Option<&'hir hir::Ty<'hir>>,
-    pub include_closures: bool,
-    pub tcx: TyCtxt<'hir>,
-}
-
-impl<'hir> FindExprBySpan<'hir> {
-    pub fn new(span: Span, tcx: TyCtxt<'hir>) -> Self {
-        Self { span, result: None, ty_result: None, tcx, include_closures: false }
-    }
-}
-
-impl<'v> Visitor<'v> for FindExprBySpan<'v> {
-    type NestedFilter = rustc_middle::hir::nested_filter::OnlyBodies;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.tcx.hir()
-    }
-
-    fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
-        if self.span == ex.span {
-            self.result = Some(ex);
-        } else {
-            if let hir::ExprKind::Closure(..) = ex.kind
-                && self.include_closures
-                && let closure_header_sp = self.span.with_hi(ex.span.hi())
-                && closure_header_sp == ex.span
-            {
-                self.result = Some(ex);
-            }
-            hir::intravisit::walk_expr(self, ex);
-        }
-    }
-
-    fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
-        if self.span == ty.span {
-            self.ty_result = Some(ty);
-        } else {
-            hir::intravisit::walk_ty(self, ty);
-        }
-    }
-}
-
-/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
-/// `param: ?Sized` would be a valid constraint.
-struct FindTypeParam {
-    param: rustc_span::Symbol,
-    invalid_spans: Vec<Span>,
-    nested: bool,
-}
-
-impl<'v> Visitor<'v> for FindTypeParam {
-    fn visit_where_predicate(&mut self, _: &'v hir::WherePredicate<'v>) {
-        // Skip where-clauses, to avoid suggesting indirection for type parameters found there.
-    }
-
-    fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
-        // We collect the spans of all uses of the "bare" type param, like in `field: T` or
-        // `field: (T, T)` where we could make `T: ?Sized` while skipping cases that are known to be
-        // valid like `field: &'a T` or `field: *mut T` and cases that *might* have further `Sized`
-        // obligations like `Box<T>` and `Vec<T>`, but we perform no extra analysis for those cases
-        // and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
-        // in that case should make what happened clear enough.
-        match ty.kind {
-            hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
-            hir::TyKind::Path(hir::QPath::Resolved(None, path))
-                if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
-            {
-                if !self.nested {
-                    debug!(?ty, "FindTypeParam::visit_ty");
-                    self.invalid_spans.push(ty.span);
-                }
-            }
-            hir::TyKind::Path(_) => {
-                let prev = self.nested;
-                self.nested = true;
-                hir::intravisit::walk_ty(self, ty);
-                self.nested = prev;
-            }
-            _ => {
-                hir::intravisit::walk_ty(self, ty);
-            }
-        }
-    }
-}
-
-/// Summarizes information
-#[derive(Clone)]
-pub enum ArgKind {
-    /// An argument of non-tuple type. Parameters are (name, ty)
-    Arg(String, String),
-
-    /// An argument of tuple type. For a "found" argument, the span is
-    /// the location in the source of the pattern. For an "expected"
-    /// argument, it will be None. The vector is a list of (name, ty)
-    /// strings for the components of the tuple.
-    Tuple(Option<Span>, Vec<(String, String)>),
-}
-
-impl ArgKind {
-    fn empty() -> ArgKind {
-        ArgKind::Arg("_".to_owned(), "_".to_owned())
-    }
-
-    /// Creates an `ArgKind` from the expected type of an
-    /// argument. It has no name (`_`) and an optional source span.
-    pub fn from_expected_ty(t: Ty<'_>, span: Option<Span>) -> ArgKind {
-        match t.kind() {
-            ty::Tuple(tys) => ArgKind::Tuple(
-                span,
-                tys.iter().map(|ty| ("_".to_owned(), ty.to_string())).collect::<Vec<_>>(),
-            ),
-            _ => ArgKind::Arg("_".to_owned(), t.to_string()),
-        }
-    }
-}
-
-struct HasNumericInferVisitor;
-
-impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for HasNumericInferVisitor {
-    type Result = ControlFlow<()>;
-
-    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
-        if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
-            ControlFlow::Break(())
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-}
-
-#[derive(Copy, Clone)]
-pub enum DefIdOrName {
-    DefId(DefId),
-    Name(&'static str),
-}
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index bce5c7101cc..5597c8be592 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -1,5 +1,5 @@
+use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
 use crate::infer::{InferCtxt, TyOrConstInferVar};
-use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::normalize::normalize_with_depth_to;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::obligation_forest::ProcessResult;
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 662d95db8ba..703ff2f7f16 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -6,7 +6,6 @@ pub mod auto_trait;
 pub(crate) mod coherence;
 pub mod const_evaluatable;
 mod engine;
-pub mod error_reporting;
 mod fulfill;
 pub mod misc;
 pub mod normalize;
@@ -24,10 +23,10 @@ mod util;
 pub mod vtable;
 pub mod wf;
 
+use crate::error_reporting::traits::TypeErrCtxtExt as _;
 use crate::infer::outlives::env::OutlivesEnvironment;
 use crate::infer::{InferCtxt, TyCtxtInferExt};
 use crate::regions::InferCtxtRegionExt;
-use crate::traits::error_reporting::TypeErrCtxtExt as _;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_errors::ErrorGuaranteed;
 use rustc_middle::query::Providers;
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
index a9ac0f7eb25..dda3aaaf71e 100644
--- a/compiler/rustc_trait_selection/src/traits/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -1,9 +1,9 @@
 //! Deeply normalize types using the old trait solver.
 
-use super::error_reporting::OverflowCause;
-use super::error_reporting::TypeErrCtxtExt;
 use super::SelectionContext;
 use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
+use crate::error_reporting::traits::OverflowCause;
+use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
 use crate::solve::NextSolverError;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 4d10d33fa6e..1c6993bdd37 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -187,7 +187,7 @@ fn predicates_reference_self(
 ) -> SmallVec<[Span; 1]> {
     let trait_ref = ty::Binder::dummy(ty::TraitRef::identity(tcx, trait_def_id));
     let predicates = if supertraits_only {
-        tcx.super_predicates_of(trait_def_id)
+        tcx.explicit_super_predicates_of(trait_def_id)
     } else {
         tcx.predicates_of(trait_def_id)
     };
@@ -203,7 +203,7 @@ fn bounds_reference_self(tcx: TyCtxt<'_>, trait_def_id: DefId) -> SmallVec<[Span
     tcx.associated_items(trait_def_id)
         .in_definition_order()
         .filter(|item| item.kind == ty::AssocKind::Type)
-        .flat_map(|item| tcx.explicit_item_bounds(item.def_id).instantiate_identity_iter_copied())
+        .flat_map(|item| tcx.explicit_item_bounds(item.def_id).iter_identity_copied())
         .filter_map(|c| predicate_references_self(tcx, c))
         .collect()
 }
@@ -256,7 +256,7 @@ fn super_predicates_have_non_lifetime_binders(
     if !tcx.features().non_lifetime_binders {
         return SmallVec::new();
     }
-    tcx.super_predicates_of(trait_def_id)
+    tcx.explicit_super_predicates_of(trait_def_id)
         .predicates
         .iter()
         .filter_map(|(pred, span)| pred.has_non_region_bound_vars().then_some(*span))
@@ -805,10 +805,11 @@ fn contains_illegal_self_type_reference<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
                         .unwrap()
                         .contains(&data.trait_ref(self.tcx).def_id);
 
+                    // only walk contained types if it's not a super trait
                     if is_supertrait_of_current_trait {
-                        ControlFlow::Continue(()) // do not walk contained types, do not report error, do collect $200
+                        ControlFlow::Continue(())
                     } else {
-                        t.super_visit_with(self) // DO walk contained types, POSSIBLY reporting an error
+                        t.super_visit_with(self) // POSSIBLY reporting an error
                     }
                 }
                 _ => t.super_visit_with(self), // walk contained types, if any
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index bed76b84ee0..c11e86abef8 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -2,11 +2,11 @@
 //! which folds deeply, invoking the underlying
 //! `normalize_canonicalized_projection_ty` query when it encounters projections.
 
+use crate::error_reporting::traits::OverflowCause;
+use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
 use crate::infer::at::At;
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
-use crate::traits::error_reporting::OverflowCause;
-use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::normalize::needs_normalization;
 use crate::traits::Normalized;
 use crate::traits::{BoundVarReplacer, PlaceholderReplacer, ScrubbedTraitError};
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
index b38841db923..8525215a3bc 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs
@@ -3,7 +3,6 @@ use crate::traits::wf;
 use crate::traits::ObligationCtxt;
 
 use rustc_infer::infer::canonical::Canonical;
-use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::traits::query::OutlivesBound;
 use rustc_macros::{HashStable, TypeFoldable, TypeVisitable};
@@ -12,6 +11,7 @@ use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt};
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::DUMMY_SP;
+use rustc_type_ir::outlives::{push_outlives_components, Component};
 use smallvec::{smallvec, SmallVec};
 
 #[derive(Copy, Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
@@ -284,7 +284,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>(
 /// those relationships.
 fn implied_bounds_from_components<'tcx>(
     sub_region: ty::Region<'tcx>,
-    sup_components: SmallVec<[Component<'tcx>; 4]>,
+    sup_components: SmallVec<[Component<TyCtxt<'tcx>>; 4]>,
 ) -> Vec<OutlivesBound<'tcx>> {
     sup_components
         .into_iter()
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 e36a9ca8bd1..4c3d833b0f9 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -571,7 +571,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             return;
         }
 
-        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
+        let drcx = DeepRejectCtxt::new(self.tcx(), TreatParams::ForLookup);
         let obligation_args = obligation.predicate.skip_binder().trait_ref.args;
         self.tcx().for_each_relevant_impl(
             obligation.predicate.def_id(),
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 8c5dc88184c..9508a3e8e15 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -574,7 +574,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // Check supertraits hold. This is so that their associated type bounds
         // will be checked in the code below.
         for super_trait in tcx
-            .super_predicates_of(trait_predicate.def_id())
+            .explicit_super_predicates_of(trait_predicate.def_id())
             .instantiate(tcx, trait_predicate.trait_ref.args)
             .predicates
             .into_iter()
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 68cc04bc8e6..2c4ffdabf14 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -18,9 +18,9 @@ use super::{
     TraitQueryMode,
 };
 
-use crate::infer::{InferCtxt, InferOk, TypeFreshener};
+use crate::error_reporting::traits::TypeErrCtxtOverflowExt;
+use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
 use crate::solve::InferCtxtSelectExt as _;
-use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::normalize::normalize_with_depth;
 use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
@@ -1523,7 +1523,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         // bound regions.
         let trait_ref = predicate.skip_binder().trait_ref;
 
-        coherence::trait_ref_is_knowable::<!>(self.infcx, trait_ref, |ty| Ok(ty)).unwrap()
+        coherence::trait_ref_is_knowable(self.infcx, trait_ref, |ty| Ok::<_, !>(ty)).into_ok()
     }
 
     /// Returns `true` if the global caches can be used.
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index c9bb0d330e1..6a904ef487e 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -14,6 +14,7 @@ use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
 use specialization_graph::GraphExt;
 
+use crate::error_reporting::traits::to_pretty_impl_header;
 use crate::errors::NegativePositiveConflict;
 use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
 use crate::traits::select::IntercrateAmbiguityCause;
@@ -25,8 +26,8 @@ use rustc_errors::{codes::*, Diag, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
 use rustc_middle::query::LocalCrate;
+use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{GenericArgs, GenericArgsRef};
 use rustc_session::lint::builtin::COHERENCE_LEAK_CHECK;
 use rustc_session::lint::builtin::ORDER_DEPENDENT_TRAIT_OBJECTS;
 use rustc_span::{sym, ErrorGuaranteed, Span, DUMMY_SP};
@@ -485,61 +486,3 @@ fn report_conflicting_impls<'tcx>(
         }
     }
 }
-
-/// Recovers the "impl X for Y" signature from `impl_def_id` and returns it as a
-/// string.
-pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Option<String> {
-    use std::fmt::Write;
-
-    let trait_ref = tcx.impl_trait_ref(impl_def_id)?.instantiate_identity();
-    let mut w = "impl".to_owned();
-
-    let args = GenericArgs::identity_for_item(tcx, impl_def_id);
-
-    // FIXME: Currently only handles ?Sized.
-    //        Needs to support ?Move and ?DynSized when they are implemented.
-    let mut types_without_default_bounds = FxIndexSet::default();
-    let sized_trait = tcx.lang_items().sized_trait();
-
-    let arg_names = args.iter().map(|k| k.to_string()).filter(|k| k != "'_").collect::<Vec<_>>();
-    if !arg_names.is_empty() {
-        types_without_default_bounds.extend(args.types());
-        w.push('<');
-        w.push_str(&arg_names.join(", "));
-        w.push('>');
-    }
-
-    write!(
-        w,
-        " {} for {}",
-        trait_ref.print_only_trait_path(),
-        tcx.type_of(impl_def_id).instantiate_identity()
-    )
-    .unwrap();
-
-    // The predicates will contain default bounds like `T: Sized`. We need to
-    // remove these bounds, and add `T: ?Sized` to any untouched type parameters.
-    let predicates = tcx.predicates_of(impl_def_id).predicates;
-    let mut pretty_predicates =
-        Vec::with_capacity(predicates.len() + types_without_default_bounds.len());
-
-    for (p, _) in predicates {
-        if let Some(poly_trait_ref) = p.as_trait_clause() {
-            if Some(poly_trait_ref.def_id()) == sized_trait {
-                // FIXME(#120456) - is `swap_remove` correct?
-                types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
-                continue;
-            }
-        }
-        pretty_predicates.push(p.to_string());
-    }
-
-    pretty_predicates.extend(types_without_default_bounds.iter().map(|ty| format!("{ty}: ?Sized")));
-
-    if !pretty_predicates.is_empty() {
-        write!(w, "\n  where {}", pretty_predicates.join(", ")).unwrap();
-    }
-
-    w.push(';');
-    Some(w)
-}
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index f132e36468a..951af4b0920 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -128,7 +128,7 @@ impl<'tcx> TraitAliasExpander<'tcx> {
         }
 
         // Get components of trait alias.
-        let predicates = tcx.super_predicates_of(trait_ref.def_id());
+        let predicates = tcx.explicit_super_predicates_of(trait_ref.def_id());
         debug!(?predicates);
 
         let items = predicates.predicates.iter().rev().filter_map(|(pred, span)| {
diff --git a/compiler/rustc_trait_selection/src/traits/vtable.rs b/compiler/rustc_trait_selection/src/traits/vtable.rs
index 017b0a45d1f..8f56f9c0f3e 100644
--- a/compiler/rustc_trait_selection/src/traits/vtable.rs
+++ b/compiler/rustc_trait_selection/src/traits/vtable.rs
@@ -6,7 +6,7 @@ use rustc_middle::bug;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, GenericParamDefKind, Ty, TyCtxt, Upcast, VtblEntry};
 use rustc_middle::ty::{GenericArgs, TypeVisitableExt};
-use rustc_span::{sym, Span};
+use rustc_span::{sym, Span, DUMMY_SP};
 use smallvec::{smallvec, SmallVec};
 
 use std::fmt::Debug;
@@ -117,7 +117,7 @@ fn prepare_vtable_segments_inner<'tcx, T>(
             let &(inner_most_trait_ref, _, _) = stack.last().unwrap();
 
             let mut direct_super_traits_iter = tcx
-                .super_predicates_of(inner_most_trait_ref.def_id())
+                .explicit_super_predicates_of(inner_most_trait_ref.def_id())
                 .predicates
                 .into_iter()
                 .filter_map(move |(pred, _)| {
@@ -285,13 +285,14 @@ fn vtable_entries<'tcx>(
                         return VtblEntry::Vacant;
                     }
 
-                    let instance = ty::Instance::resolve_for_vtable(
+                    let instance = ty::Instance::expect_resolve_for_vtable(
                         tcx,
                         ty::ParamEnv::reveal_all(),
                         def_id,
                         args,
-                    )
-                    .expect("resolution failed during building vtable representation");
+                        DUMMY_SP,
+                    );
+
                     VtblEntry::Method(instance)
                 });
 
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 066755f7b3e..f071dc6c784 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -437,12 +437,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
     /// Pushes the obligations required for an alias (except inherent) to be WF
     /// into `self.out`.
-    fn compute_alias_ty(&mut self, data: ty::AliasTy<'tcx>) {
-        self.compute_alias_term(data.into());
-    }
-
-    /// Pushes the obligations required for an alias (except inherent) to be WF
-    /// into `self.out`.
     fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) {
         // A projection is well-formed if
         //
@@ -498,7 +492,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
             self.out.extend(obligations);
         }
 
-        self.compute_projection_args(data.args);
+        data.args.visit_with(self);
     }
 
     fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) {
@@ -646,8 +640,6 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
-    type Result = ();
-
     fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
         debug!("wf bounds for t={:?} t.kind={:#?}", t, t.kind());
 
@@ -702,8 +694,8 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
             }
 
             ty::Alias(ty::Projection | ty::Opaque | ty::Weak, data) => {
-                self.compute_alias_ty(data);
-                return; // Subtree handled by compute_projection.
+                let obligations = self.nominal_obligations(data.def_id, data.args);
+                self.out.extend(obligations);
             }
             ty::Alias(ty::Inherent, data) => {
                 self.compute_inherent_projection(data);
diff --git a/compiler/rustc_traits/src/codegen.rs b/compiler/rustc_traits/src/codegen.rs
index c73ececd1d1..3ee5fd876ff 100644
--- a/compiler/rustc_traits/src/codegen.rs
+++ b/compiler/rustc_traits/src/codegen.rs
@@ -7,7 +7,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::bug;
 use rustc_middle::traits::CodegenObligationError;
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt;
 use rustc_trait_selection::traits::{
     ImplSource, Obligation, ObligationCause, ObligationCtxt, ScrubbedTraitError, SelectionContext,
     Unimplemented,
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 0430e0bb70e..2d70fdc3935 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -2,8 +2,8 @@ use rustc_infer::infer::canonical::{Canonical, QueryResponse};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
+use rustc_trait_selection::error_reporting::traits::TypeErrCtxtOverflowExt;
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
-use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::{
     normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
 };
diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs
index f1dd94839fe..f078cfe1b25 100644
--- a/compiler/rustc_ty_utils/src/abi.rs
+++ b/compiler/rustc_ty_utils/src/abi.rs
@@ -127,9 +127,9 @@ fn fn_sig_for_fn_abi<'tcx>(
                     coroutine_kind = ty::ClosureKind::FnOnce;
 
                     // Implementations of `FnMut` and `Fn` for coroutine-closures
-                    // still take their receiver by (mut) ref.
+                    // still take their receiver by ref.
                     if receiver_by_ref {
-                        Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty)
+                        Ty::new_imm_ref(tcx, tcx.lifetimes.re_erased, coroutine_ty)
                     } else {
                         coroutine_ty
                     }
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
index 4395eb57cd6..6726db8bb54 100644
--- a/compiler/rustc_ty_utils/src/assoc.rs
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -4,8 +4,9 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::query::Providers;
-use rustc_middle::ty::{self, ImplTraitInTraitData, TyCtxt};
+use rustc_middle::ty::{self, ImplTraitInTraitData, Ty, TyCtxt};
 use rustc_middle::{bug, span_bug};
+use rustc_span::sym;
 use rustc_span::symbol::kw;
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -14,6 +15,7 @@ pub(crate) fn provide(providers: &mut Providers) {
         associated_item_def_ids,
         associated_items,
         associated_types_for_impl_traits_in_associated_fn,
+        associated_type_for_effects,
         associated_type_for_impl_trait_in_trait,
         impl_item_implementor_ids,
         ..*providers
@@ -44,7 +46,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
                                 )
                             })
                             .copied(),
-                    ),
+                    )
+                    .chain(tcx.associated_type_for_effects(def_id)),
             )
         }
         hir::ItemKind::Impl(impl_) => {
@@ -70,7 +73,8 @@ fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &[DefId] {
                                 )
                             })
                             .copied()
-                    })),
+                    }))
+                    .chain(tcx.associated_type_for_effects(def_id)),
             )
         }
         _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
@@ -143,6 +147,7 @@ fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty
         container: ty::TraitContainer,
         fn_has_self_parameter: has_self,
         opt_rpitit_info: None,
+        is_effects_desugaring: false,
     }
 }
 
@@ -162,9 +167,138 @@ fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::A
         container: ty::ImplContainer,
         fn_has_self_parameter: has_self,
         opt_rpitit_info: None,
+        is_effects_desugaring: false,
     }
 }
 
+/// Given an `def_id` of a trait or a trait impl:
+///
+/// If `def_id` is a trait that has `#[const_trait]`, then it synthesizes
+/// a new def id corresponding to a new associated type for the effects.
+///
+/// If `def_id` is an impl, then synthesize the associated type according
+/// to the constness of the impl.
+fn associated_type_for_effects(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
+    // don't synthesize the associated type even if the user has written `const_trait`
+    // if the effects feature is disabled.
+    if !tcx.features().effects {
+        return None;
+    }
+    let (feed, parent_did) = match tcx.def_kind(def_id) {
+        DefKind::Trait => {
+            let trait_def_id = def_id;
+            let attr = tcx.get_attr(def_id, sym::const_trait)?;
+
+            let span = attr.span;
+            let trait_assoc_ty = tcx.at(span).create_def(trait_def_id, kw::Empty, DefKind::AssocTy);
+
+            let local_def_id = trait_assoc_ty.def_id();
+            let def_id = local_def_id.to_def_id();
+
+            // Copy span of the attribute.
+            trait_assoc_ty.def_ident_span(Some(span));
+
+            trait_assoc_ty.associated_item(ty::AssocItem {
+                name: kw::Empty,
+                kind: ty::AssocKind::Type,
+                def_id,
+                trait_item_def_id: None,
+                container: ty::TraitContainer,
+                fn_has_self_parameter: false,
+                opt_rpitit_info: None,
+                is_effects_desugaring: true,
+            });
+
+            // No default type
+            trait_assoc_ty.defaultness(hir::Defaultness::Default { has_value: false });
+
+            trait_assoc_ty.is_type_alias_impl_trait(false);
+
+            (trait_assoc_ty, trait_def_id)
+        }
+        DefKind::Impl { .. } => {
+            let impl_def_id = def_id;
+            let trait_id = tcx.trait_id_of_impl(def_id.to_def_id())?;
+
+            // first get the DefId of the assoc type on the trait, if there is not,
+            // then we don't need to generate it on the impl.
+            let trait_assoc_id = tcx.associated_type_for_effects(trait_id)?;
+
+            // FIXME(effects): span
+            let span = tcx.def_ident_span(def_id).unwrap();
+
+            let impl_assoc_ty = tcx.at(span).create_def(def_id, kw::Empty, DefKind::AssocTy);
+
+            let local_def_id = impl_assoc_ty.def_id();
+            let def_id = local_def_id.to_def_id();
+
+            impl_assoc_ty.def_ident_span(Some(span));
+
+            impl_assoc_ty.associated_item(ty::AssocItem {
+                name: kw::Empty,
+                kind: ty::AssocKind::Type,
+                def_id,
+                trait_item_def_id: Some(trait_assoc_id),
+                container: ty::ImplContainer,
+                fn_has_self_parameter: false,
+                opt_rpitit_info: None,
+                is_effects_desugaring: true,
+            });
+
+            // no default value.
+            impl_assoc_ty.defaultness(hir::Defaultness::Final);
+
+            // set the type of the associated type! If this is a const impl,
+            // we set to Maybe, otherwise we set to `Runtime`.
+            let type_def_id = if tcx.is_const_trait_impl_raw(impl_def_id.to_def_id()) {
+                tcx.require_lang_item(hir::LangItem::EffectsMaybe, Some(span))
+            } else {
+                tcx.require_lang_item(hir::LangItem::EffectsRuntime, Some(span))
+            };
+            // FIXME(effects): make impls use `Min` for their effect types
+            impl_assoc_ty.type_of(ty::EarlyBinder::bind(Ty::new_adt(
+                tcx,
+                tcx.adt_def(type_def_id),
+                ty::GenericArgs::empty(),
+            )));
+
+            (impl_assoc_ty, impl_def_id)
+        }
+        def_kind => bug!(
+            "associated_type_for_effects: {:?} should be Trait or Impl but is {:?}",
+            def_id,
+            def_kind
+        ),
+    };
+
+    feed.feed_hir();
+
+    // visibility is public.
+    feed.visibility(ty::Visibility::Public);
+
+    // Copy generics_of of the trait/impl, making the trait/impl as parent.
+    feed.generics_of({
+        let parent_generics = tcx.generics_of(parent_did);
+        let parent_count = parent_generics.parent_count + parent_generics.own_params.len();
+
+        ty::Generics {
+            parent: Some(parent_did.to_def_id()),
+            parent_count,
+            own_params: vec![],
+            param_def_id_to_index: parent_generics.param_def_id_to_index.clone(),
+            has_self: false,
+            has_late_bound_regions: None,
+            host_effect_index: parent_generics.host_effect_index,
+        }
+    });
+    feed.explicit_item_super_predicates(ty::EarlyBinder::bind(&[]));
+
+    // There are no inferred outlives for the synthesized associated type.
+    feed.inferred_outlives_of(&[]);
+
+    Some(feed.def_id().to_def_id())
+}
+
 /// Given an `fn_def_id` of a trait or a trait implementation:
 ///
 /// if `fn_def_id` is a function defined inside a trait, then it synthesizes
@@ -275,6 +409,7 @@ fn associated_type_for_impl_trait_in_trait(
             fn_def_id: fn_def_id.to_def_id(),
             opaque_def_id: opaque_ty_def_id.to_def_id(),
         }),
+        is_effects_desugaring: false,
     });
 
     // Copy visility of the containing function.
@@ -326,6 +461,7 @@ fn associated_type_for_impl_trait_in_impl(
         container: ty::ImplContainer,
         fn_has_self_parameter: false,
         opt_rpitit_info: Some(ImplTraitInTraitData::Impl { fn_def_id: impl_fn_def_id.to_def_id() }),
+        is_effects_desugaring: false,
     });
 
     // Copy visility of the containing function.
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index c50a490a9dc..7b6d86d22a5 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -16,7 +16,7 @@ use traits::{translate_args, Reveal};
 
 use crate::errors::UnexpectedFnPtrAssociatedItem;
 
-fn resolve_instance<'tcx>(
+fn resolve_instance_raw<'tcx>(
     tcx: TyCtxt<'tcx>,
     key: ty::ParamEnvAnd<'tcx, (DefId, GenericArgsRef<'tcx>)>,
 ) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
@@ -364,5 +364,5 @@ fn resolve_associated_item<'tcx>(
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
-    *providers = Providers { resolve_instance, ..*providers };
+    *providers = Providers { resolve_instance_raw, ..*providers };
 }
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 686f2f04ad9..5e91320f897 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -169,10 +169,8 @@ impl<'tcx> OpaqueTypeCollector<'tcx> {
                 // Collect opaque types nested within the associated type bounds of this opaque type.
                 // We use identity args here, because we already know that the opaque type uses
                 // only generic parameters, and thus instantiating would not give us more information.
-                for (pred, span) in self
-                    .tcx
-                    .explicit_item_bounds(alias_ty.def_id)
-                    .instantiate_identity_iter_copied()
+                for (pred, span) in
+                    self.tcx.explicit_item_bounds(alias_ty.def_id).iter_identity_copied()
                 {
                     trace!(?pred);
                     self.visit_spanned(span, pred);
diff --git a/compiler/rustc_ty_utils/src/sig_types.rs b/compiler/rustc_ty_utils/src/sig_types.rs
index 97a1b94263e..eb6cb369974 100644
--- a/compiler/rustc_ty_utils/src/sig_types.rs
+++ b/compiler/rustc_ty_utils/src/sig_types.rs
@@ -62,7 +62,7 @@ pub fn walk_types<'tcx, V: SpannedTypeVisitor<'tcx>>(
             }
         }
         DefKind::OpaqueTy => {
-            for (pred, span) in tcx.explicit_item_bounds(item).instantiate_identity_iter_copied() {
+            for (pred, span) in tcx.explicit_item_bounds(item).iter_identity_copied() {
                 try_visit!(visitor.visit(span, pred));
             }
         }
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 491ef34430c..2531219baec 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -448,7 +448,7 @@ where
 
     /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
     /// but on an iterator of `TypeFoldable` values.
-    pub fn instantiate_identity_iter(self) -> Iter::IntoIter {
+    pub fn iter_identity(self) -> Iter::IntoIter {
         self.value.into_iter()
     }
 }
@@ -515,9 +515,7 @@ where
 
     /// Similar to [`instantiate_identity`](EarlyBinder::instantiate_identity),
     /// but on an iterator of values that deref to a `TypeFoldable`.
-    pub fn instantiate_identity_iter_copied(
-        self,
-    ) -> impl Iterator<Item = <Iter::Item as Deref>::Target> {
+    pub fn iter_identity_copied(self) -> impl Iterator<Item = <Iter::Item as Deref>::Target> {
         self.value.into_iter().map(|v| *v)
     }
 }
diff --git a/compiler/rustc_type_ir/src/effects.rs b/compiler/rustc_type_ir/src/effects.rs
new file mode 100644
index 00000000000..f7942f2f982
--- /dev/null
+++ b/compiler/rustc_type_ir/src/effects.rs
@@ -0,0 +1,59 @@
+use crate::inherent::{AdtDef, IntoKind, Ty};
+use crate::lang_items::TraitSolverLangItem::{EffectsMaybe, EffectsNoRuntime, EffectsRuntime};
+use crate::Interner;
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum EffectKind {
+    Maybe,
+    Runtime,
+    NoRuntime,
+}
+
+impl EffectKind {
+    pub fn try_from_def_id<I: Interner>(tcx: I, def_id: I::DefId) -> Option<EffectKind> {
+        if tcx.is_lang_item(def_id, EffectsMaybe) {
+            Some(EffectKind::Maybe)
+        } else if tcx.is_lang_item(def_id, EffectsRuntime) {
+            Some(EffectKind::Runtime)
+        } else if tcx.is_lang_item(def_id, EffectsNoRuntime) {
+            Some(EffectKind::NoRuntime)
+        } else {
+            None
+        }
+    }
+
+    pub fn to_def_id<I: Interner>(self, tcx: I) -> I::DefId {
+        let lang_item = match self {
+            EffectKind::Maybe => EffectsMaybe,
+            EffectKind::NoRuntime => EffectsNoRuntime,
+            EffectKind::Runtime => EffectsRuntime,
+        };
+
+        tcx.require_lang_item(lang_item)
+    }
+
+    pub fn try_from_ty<I: Interner>(tcx: I, ty: I::Ty) -> Option<EffectKind> {
+        if let crate::Adt(def, _) = ty.kind() {
+            Self::try_from_def_id(tcx, def.def_id())
+        } else {
+            None
+        }
+    }
+
+    pub fn to_ty<I: Interner>(self, tcx: I) -> I::Ty {
+        I::Ty::new_adt(tcx, tcx.adt_def(self.to_def_id(tcx)), Default::default())
+    }
+
+    /// Returns an intersection between two effect kinds. If one effect kind
+    /// is more permissive than the other (e.g. `Maybe` vs `Runtime`), this
+    /// returns the less permissive effect kind (`Runtime`).
+    pub fn intersection(a: Self, b: Self) -> Option<Self> {
+        use EffectKind::*;
+        match (a, b) {
+            (Maybe, x) | (x, Maybe) => Some(x),
+            (Runtime, Runtime) => Some(Runtime),
+            (NoRuntime, NoRuntime) => Some(NoRuntime),
+            (Runtime, NoRuntime) | (NoRuntime, Runtime) => None,
+        }
+    }
+}
diff --git a/compiler/rustc_type_ir/src/elaborate.rs b/compiler/rustc_type_ir/src/elaborate.rs
new file mode 100644
index 00000000000..0def7d12f74
--- /dev/null
+++ b/compiler/rustc_type_ir/src/elaborate.rs
@@ -0,0 +1,305 @@
+use std::marker::PhantomData;
+
+use smallvec::smallvec;
+
+use crate::data_structures::HashSet;
+use crate::outlives::{push_outlives_components, Component};
+use crate::{self as ty, Interner};
+use crate::{inherent::*, Upcast as _};
+
+/// "Elaboration" is the process of identifying all the predicates that
+/// are implied by a source predicate. Currently, this basically means
+/// walking the "supertraits" and other similar assumptions. For example,
+/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd`
+/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that
+/// `T: Foo`, then we know that `T: 'static`.
+pub struct Elaborator<I: Interner, O> {
+    cx: I,
+    stack: Vec<O>,
+    visited: HashSet<ty::Binder<I, ty::PredicateKind<I>>>,
+    mode: Filter,
+}
+
+enum Filter {
+    All,
+    OnlySelf,
+}
+
+/// Describes how to elaborate an obligation into a sub-obligation.
+pub trait Elaboratable<I: Interner> {
+    fn predicate(&self) -> I::Predicate;
+
+    // Makes a new `Self` but with a different clause that comes from elaboration.
+    fn child(&self, clause: I::Clause) -> Self;
+
+    // Makes a new `Self` but with a different clause and a different cause
+    // code (if `Self` has one, such as [`PredicateObligation`]).
+    fn child_with_derived_cause(
+        &self,
+        clause: I::Clause,
+        span: I::Span,
+        parent_trait_pred: ty::Binder<I, ty::TraitPredicate<I>>,
+        index: usize,
+    ) -> Self;
+}
+
+pub fn elaborate<I: Interner, O: Elaboratable<I>>(
+    cx: I,
+    obligations: impl IntoIterator<Item = O>,
+) -> Elaborator<I, O> {
+    let mut elaborator =
+        Elaborator { cx, stack: Vec::new(), visited: HashSet::default(), mode: Filter::All };
+    elaborator.extend_deduped(obligations);
+    elaborator
+}
+
+impl<I: Interner, O: Elaboratable<I>> Elaborator<I, O> {
+    fn extend_deduped(&mut self, obligations: impl IntoIterator<Item = O>) {
+        // Only keep those bounds that we haven't already seen.
+        // This is necessary to prevent infinite recursion in some
+        // cases. One common case is when people define
+        // `trait Sized: Sized { }` rather than `trait Sized { }`.
+        self.stack.extend(
+            obligations.into_iter().filter(|o| {
+                self.visited.insert(self.cx.anonymize_bound_vars(o.predicate().kind()))
+            }),
+        );
+    }
+
+    /// Filter to only the supertraits of trait predicates, i.e. only the predicates
+    /// that have `Self` as their self type, instead of all implied predicates.
+    pub fn filter_only_self(mut self) -> Self {
+        self.mode = Filter::OnlySelf;
+        self
+    }
+
+    fn elaborate(&mut self, elaboratable: &O) {
+        let cx = self.cx;
+
+        // We only elaborate clauses.
+        let Some(clause) = elaboratable.predicate().as_clause() else {
+            return;
+        };
+
+        let bound_clause = clause.kind();
+        match bound_clause.skip_binder() {
+            ty::ClauseKind::Trait(data) => {
+                // Negative trait bounds do not imply any supertrait bounds
+                if data.polarity != ty::PredicatePolarity::Positive {
+                    return;
+                }
+
+                let map_to_child_clause =
+                    |(index, (clause, span)): (usize, (I::Clause, I::Span))| {
+                        elaboratable.child_with_derived_cause(
+                            clause.instantiate_supertrait(cx, bound_clause.rebind(data.trait_ref)),
+                            span,
+                            bound_clause.rebind(data),
+                            index,
+                        )
+                    };
+
+                // Get predicates implied by the trait, or only super predicates if we only care about self predicates.
+                match self.mode {
+                    Filter::All => self.extend_deduped(
+                        cx.explicit_implied_predicates_of(data.def_id())
+                            .iter_identity()
+                            .enumerate()
+                            .map(map_to_child_clause),
+                    ),
+                    Filter::OnlySelf => self.extend_deduped(
+                        cx.explicit_super_predicates_of(data.def_id())
+                            .iter_identity()
+                            .enumerate()
+                            .map(map_to_child_clause),
+                    ),
+                };
+            }
+            ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => {
+                // We know that `T: 'a` for some type `T`. We can
+                // often elaborate this. For example, if we know that
+                // `[U]: 'a`, that implies that `U: 'a`. Similarly, if
+                // we know `&'a U: 'b`, then we know that `'a: 'b` and
+                // `U: 'b`.
+                //
+                // We can basically ignore bound regions here. So for
+                // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to
+                // `'a: 'b`.
+
+                // Ignore `for<'a> T: 'a` -- we might in the future
+                // consider this as evidence that `T: 'static`, but
+                // I'm a bit wary of such constructions and so for now
+                // I want to be conservative. --nmatsakis
+                if r_min.is_bound() {
+                    return;
+                }
+
+                let mut components = smallvec![];
+                push_outlives_components(cx, ty_max, &mut components);
+                self.extend_deduped(
+                    components
+                        .into_iter()
+                        .filter_map(|component| elaborate_component_to_clause(cx, component, r_min))
+                        .map(|clause| elaboratable.child(bound_clause.rebind(clause).upcast(cx))),
+                );
+            }
+            ty::ClauseKind::RegionOutlives(..) => {
+                // Nothing to elaborate from `'a: 'b`.
+            }
+            ty::ClauseKind::WellFormed(..) => {
+                // Currently, we do not elaborate WF predicates,
+                // although we easily could.
+            }
+            ty::ClauseKind::Projection(..) => {
+                // Nothing to elaborate in a projection predicate.
+            }
+            ty::ClauseKind::ConstEvaluatable(..) => {
+                // Currently, we do not elaborate const-evaluatable
+                // predicates.
+            }
+            ty::ClauseKind::ConstArgHasType(..) => {
+                // Nothing to elaborate
+            }
+        }
+    }
+}
+
+fn elaborate_component_to_clause<I: Interner>(
+    cx: I,
+    component: Component<I>,
+    outlives_region: I::Region,
+) -> Option<ty::ClauseKind<I>> {
+    match component {
+        Component::Region(r) => {
+            if r.is_bound() {
+                None
+            } else {
+                Some(ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(r, outlives_region)))
+            }
+        }
+
+        Component::Param(p) => {
+            let ty = Ty::new_param(cx, p);
+            Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region)))
+        }
+
+        Component::Placeholder(p) => {
+            let ty = Ty::new_placeholder(cx, p);
+            Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, outlives_region)))
+        }
+
+        Component::UnresolvedInferenceVariable(_) => None,
+
+        Component::Alias(alias_ty) => {
+            // We might end up here if we have `Foo<<Bar as Baz>::Assoc>: 'a`.
+            // With this, we can deduce that `<Bar as Baz>::Assoc: 'a`.
+            Some(ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(
+                alias_ty.to_ty(cx),
+                outlives_region,
+            )))
+        }
+
+        Component::EscapingAlias(_) => {
+            // We might be able to do more here, but we don't
+            // want to deal with escaping vars right now.
+            None
+        }
+    }
+}
+
+impl<I: Interner, O: Elaboratable<I>> Iterator for Elaborator<I, O> {
+    type Item = O;
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        (self.stack.len(), None)
+    }
+
+    fn next(&mut self) -> Option<Self::Item> {
+        // Extract next item from top-most stack frame, if any.
+        if let Some(obligation) = self.stack.pop() {
+            self.elaborate(&obligation);
+            Some(obligation)
+        } else {
+            None
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Supertrait iterator
+///////////////////////////////////////////////////////////////////////////
+
+/// Computes the def-ids of the transitive supertraits of `trait_def_id`. This (intentionally)
+/// does not compute the full elaborated super-predicates but just the set of def-ids. It is used
+/// to identify which traits may define a given associated type to help avoid cycle errors,
+/// and to make size estimates for vtable layout computation.
+pub fn supertrait_def_ids<I: Interner>(
+    cx: I,
+    trait_def_id: I::DefId,
+) -> impl Iterator<Item = I::DefId> {
+    let mut set = HashSet::default();
+    let mut stack = vec![trait_def_id];
+
+    set.insert(trait_def_id);
+
+    std::iter::from_fn(move || {
+        let trait_def_id = stack.pop()?;
+
+        for (predicate, _) in cx.explicit_super_predicates_of(trait_def_id).iter_identity() {
+            if let ty::ClauseKind::Trait(data) = predicate.kind().skip_binder() {
+                if set.insert(data.def_id()) {
+                    stack.push(data.def_id());
+                }
+            }
+        }
+
+        Some(trait_def_id)
+    })
+}
+
+pub fn supertraits<I: Interner>(
+    tcx: I,
+    trait_ref: ty::Binder<I, ty::TraitRef<I>>,
+) -> FilterToTraits<I, Elaborator<I, I::Clause>> {
+    elaborate(tcx, [trait_ref.upcast(tcx)]).filter_only_self().filter_to_traits()
+}
+
+pub fn transitive_bounds<I: Interner>(
+    tcx: I,
+    trait_refs: impl Iterator<Item = ty::Binder<I, ty::TraitRef<I>>>,
+) -> FilterToTraits<I, Elaborator<I, I::Clause>> {
+    elaborate(tcx, trait_refs.map(|trait_ref| trait_ref.upcast(tcx)))
+        .filter_only_self()
+        .filter_to_traits()
+}
+
+impl<I: Interner> Elaborator<I, I::Clause> {
+    fn filter_to_traits(self) -> FilterToTraits<I, Self> {
+        FilterToTraits { _cx: PhantomData, base_iterator: self }
+    }
+}
+
+/// A filter around an iterator of predicates that makes it yield up
+/// just trait references.
+pub struct FilterToTraits<I: Interner, It: Iterator<Item = I::Clause>> {
+    _cx: PhantomData<I>,
+    base_iterator: It,
+}
+
+impl<I: Interner, It: Iterator<Item = I::Clause>> Iterator for FilterToTraits<I, It> {
+    type Item = ty::Binder<I, ty::TraitRef<I>>;
+
+    fn next(&mut self) -> Option<ty::Binder<I, ty::TraitRef<I>>> {
+        while let Some(pred) = self.base_iterator.next() {
+            if let Some(data) = pred.as_trait_clause() {
+                return Some(data.map_bound(|t| t.trait_ref));
+            }
+        }
+        None
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        let (_, upper) = self.base_iterator.size_hint();
+        (0, upper)
+    }
+}
diff --git a/compiler/rustc_type_ir/src/fast_reject.rs b/compiler/rustc_type_ir/src/fast_reject.rs
new file mode 100644
index 00000000000..0810fa5c558
--- /dev/null
+++ b/compiler/rustc_type_ir/src/fast_reject.rs
@@ -0,0 +1,397 @@
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::iter;
+use std::marker::PhantomData;
+
+use rustc_ast_ir::Mutability;
+#[cfg(feature = "nightly")]
+use rustc_data_structures::fingerprint::Fingerprint;
+#[cfg(feature = "nightly")]
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
+#[cfg(feature = "nightly")]
+use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
+
+use crate::inherent::*;
+use crate::visit::TypeVisitableExt as _;
+use crate::{self as ty, Interner};
+
+/// See `simplify_type`.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
+pub enum SimplifiedType<DefId> {
+    Bool,
+    Char,
+    Int(ty::IntTy),
+    Uint(ty::UintTy),
+    Float(ty::FloatTy),
+    Adt(DefId),
+    Foreign(DefId),
+    Str,
+    Array,
+    Slice,
+    Ref(Mutability),
+    Ptr(Mutability),
+    Never,
+    Tuple(usize),
+    /// A trait object, all of whose components are markers
+    /// (e.g., `dyn Send + Sync`).
+    MarkerTraitObject,
+    Trait(DefId),
+    Closure(DefId),
+    Coroutine(DefId),
+    CoroutineWitness(DefId),
+    Function(usize),
+    Placeholder,
+    Error,
+}
+
+#[cfg(feature = "nightly")]
+impl<HCX: Clone, DefId: HashStable<HCX>> ToStableHashKey<HCX> for SimplifiedType<DefId> {
+    type KeyType = Fingerprint;
+
+    #[inline]
+    fn to_stable_hash_key(&self, hcx: &HCX) -> Fingerprint {
+        let mut hasher = StableHasher::new();
+        let mut hcx: HCX = hcx.clone();
+        self.hash_stable(&mut hcx, &mut hasher);
+        hasher.finish()
+    }
+}
+
+/// Generic parameters are pretty much just bound variables, e.g.
+/// the type of `fn foo<'a, T>(x: &'a T) -> u32 { ... }` can be thought of as
+/// `for<'a, T> fn(&'a T) -> u32`.
+///
+/// Typecheck of `foo` has to succeed for all possible generic arguments, so
+/// during typeck, we have to treat its generic parameters as if they
+/// were placeholders.
+///
+/// But when calling `foo` we only have to provide a specific generic argument.
+/// In that case the generic parameters are instantiated with inference variables.
+/// As we use `simplify_type` before that instantiation happens, we just treat
+/// generic parameters as if they were inference variables in that case.
+#[derive(PartialEq, Eq, Debug, Clone, Copy)]
+pub enum TreatParams {
+    /// Treat parameters as infer vars. This is the correct mode for caching
+    /// an impl's type for lookup.
+    AsCandidateKey,
+    /// Treat parameters as placeholders in the given environment. This is the
+    /// correct mode for *lookup*, as during candidate selection.
+    ///
+    /// This also treats projections with inference variables as infer vars
+    /// since they could be further normalized.
+    ForLookup,
+}
+
+/// Tries to simplify a type by only returning the outermost injective¹ layer, if one exists.
+///
+/// **This function should only be used if you need to store or retrieve the type from some
+/// hashmap. If you want to quickly decide whether two types may unify, use the [DeepRejectCtxt]
+/// instead.**
+///
+/// The idea is to get something simple that we can use to quickly decide if two types could unify,
+/// for example during method lookup. If this function returns `Some(x)` it can only unify with
+/// types for which this method returns either `Some(x)` as well or `None`.
+///
+/// A special case here are parameters and projections, which are only injective
+/// if they are treated as placeholders.
+///
+/// For example when storing impls based on their simplified self type, we treat
+/// generic parameters as if they were inference variables. We must not simplify them here,
+/// as they can unify with any other type.
+///
+/// With projections we have to be even more careful, as treating them as placeholders
+/// is only correct if they are fully normalized.
+///
+/// ¹ meaning that if the outermost layers are different, then the whole types are also different.
+pub fn simplify_type<I: Interner>(
+    tcx: I,
+    ty: I::Ty,
+    treat_params: TreatParams,
+) -> Option<SimplifiedType<I::DefId>> {
+    match ty.kind() {
+        ty::Bool => Some(SimplifiedType::Bool),
+        ty::Char => Some(SimplifiedType::Char),
+        ty::Int(int_type) => Some(SimplifiedType::Int(int_type)),
+        ty::Uint(uint_type) => Some(SimplifiedType::Uint(uint_type)),
+        ty::Float(float_type) => Some(SimplifiedType::Float(float_type)),
+        ty::Adt(def, _) => Some(SimplifiedType::Adt(def.def_id())),
+        ty::Str => Some(SimplifiedType::Str),
+        ty::Array(..) => Some(SimplifiedType::Array),
+        ty::Slice(..) => Some(SimplifiedType::Slice),
+        ty::Pat(ty, ..) => simplify_type(tcx, ty, treat_params),
+        ty::RawPtr(_, mutbl) => Some(SimplifiedType::Ptr(mutbl)),
+        ty::Dynamic(trait_info, ..) => match trait_info.principal_def_id() {
+            Some(principal_def_id) if !tcx.trait_is_auto(principal_def_id) => {
+                Some(SimplifiedType::Trait(principal_def_id))
+            }
+            _ => Some(SimplifiedType::MarkerTraitObject),
+        },
+        ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
+        ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => {
+            Some(SimplifiedType::Closure(def_id))
+        }
+        ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)),
+        ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)),
+        ty::Never => Some(SimplifiedType::Never),
+        ty::Tuple(tys) => Some(SimplifiedType::Tuple(tys.len())),
+        ty::FnPtr(f) => Some(SimplifiedType::Function(f.skip_binder().inputs().len())),
+        ty::Placeholder(..) => Some(SimplifiedType::Placeholder),
+        ty::Param(_) => match treat_params {
+            TreatParams::ForLookup => Some(SimplifiedType::Placeholder),
+            TreatParams::AsCandidateKey => None,
+        },
+        ty::Alias(..) => match treat_params {
+            // When treating `ty::Param` as a placeholder, projections also
+            // don't unify with anything else as long as they are fully normalized.
+            // FIXME(-Znext-solver): Can remove this `if` and always simplify to `Placeholder`
+            // when the new solver is enabled by default.
+            TreatParams::ForLookup if !ty.has_non_region_infer() => {
+                Some(SimplifiedType::Placeholder)
+            }
+            TreatParams::ForLookup | TreatParams::AsCandidateKey => None,
+        },
+        ty::Foreign(def_id) => Some(SimplifiedType::Foreign(def_id)),
+        ty::Error(_) => Some(SimplifiedType::Error),
+        ty::Bound(..) | ty::Infer(_) => None,
+    }
+}
+
+impl<DefId> SimplifiedType<DefId> {
+    pub fn def(self) -> Option<DefId> {
+        match self {
+            SimplifiedType::Adt(d)
+            | SimplifiedType::Foreign(d)
+            | SimplifiedType::Trait(d)
+            | SimplifiedType::Closure(d)
+            | SimplifiedType::Coroutine(d)
+            | SimplifiedType::CoroutineWitness(d) => Some(d),
+            _ => None,
+        }
+    }
+}
+
+/// Given generic arguments from an obligation and an impl,
+/// could these two be unified after replacing parameters in the
+/// the impl with inference variables.
+///
+/// For obligations, parameters won't be replaced by inference
+/// variables and only unify with themselves. We treat them
+/// the same way we treat placeholders.
+///
+/// We also use this function during coherence. For coherence the
+/// impls only have to overlap for some value, so we treat parameters
+/// on both sides like inference variables. This behavior is toggled
+/// using the `treat_obligation_params` field.
+#[derive(Debug, Clone, Copy)]
+pub struct DeepRejectCtxt<I: Interner> {
+    treat_obligation_params: TreatParams,
+    _interner: PhantomData<I>,
+}
+
+impl<I: Interner> DeepRejectCtxt<I> {
+    pub fn new(_interner: I, treat_obligation_params: TreatParams) -> Self {
+        DeepRejectCtxt { treat_obligation_params, _interner: PhantomData }
+    }
+
+    pub fn args_may_unify(
+        self,
+        obligation_args: I::GenericArgs,
+        impl_args: I::GenericArgs,
+    ) -> bool {
+        iter::zip(obligation_args.iter(), impl_args.iter()).all(|(obl, imp)| {
+            match (obl.kind(), imp.kind()) {
+                // We don't fast reject based on regions.
+                (ty::GenericArgKind::Lifetime(_), ty::GenericArgKind::Lifetime(_)) => true,
+                (ty::GenericArgKind::Type(obl), ty::GenericArgKind::Type(imp)) => {
+                    self.types_may_unify(obl, imp)
+                }
+                (ty::GenericArgKind::Const(obl), ty::GenericArgKind::Const(imp)) => {
+                    self.consts_may_unify(obl, imp)
+                }
+                _ => panic!("kind mismatch: {obl:?} {imp:?}"),
+            }
+        })
+    }
+
+    pub fn types_may_unify(self, obligation_ty: I::Ty, impl_ty: I::Ty) -> bool {
+        match impl_ty.kind() {
+            // Start by checking whether the type in the impl may unify with
+            // pretty much everything. Just return `true` in that case.
+            ty::Param(_) | ty::Error(_) | ty::Alias(..) => return true,
+            // These types only unify with inference variables or their own
+            // variant.
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(..)
+            | ty::Str
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::Dynamic(..)
+            | ty::Pat(..)
+            | ty::Ref(..)
+            | ty::Never
+            | ty::Tuple(..)
+            | ty::FnPtr(..)
+            | ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()),
+            ty::FnDef(..)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(..)
+            | ty::CoroutineWitness(..)
+            | ty::Placeholder(..)
+            | ty::Bound(..)
+            | ty::Infer(_) => panic!("unexpected impl_ty: {impl_ty:?}"),
+        }
+
+        let k = impl_ty.kind();
+        match obligation_ty.kind() {
+            // Purely rigid types, use structural equivalence.
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Str
+            | ty::Never
+            | ty::Foreign(_) => obligation_ty == impl_ty,
+            ty::Ref(_, obl_ty, obl_mutbl) => match k {
+                ty::Ref(_, impl_ty, impl_mutbl) => {
+                    obl_mutbl == impl_mutbl && self.types_may_unify(obl_ty, impl_ty)
+                }
+                _ => false,
+            },
+            ty::Adt(obl_def, obl_args) => match k {
+                ty::Adt(impl_def, impl_args) => {
+                    obl_def == impl_def && self.args_may_unify(obl_args, impl_args)
+                }
+                _ => false,
+            },
+            ty::Pat(obl_ty, _) => {
+                // FIXME(pattern_types): take pattern into account
+                matches!(k, ty::Pat(impl_ty, _) if self.types_may_unify(obl_ty, impl_ty))
+            }
+            ty::Slice(obl_ty) => {
+                matches!(k, ty::Slice(impl_ty) if self.types_may_unify(obl_ty, impl_ty))
+            }
+            ty::Array(obl_ty, obl_len) => match k {
+                ty::Array(impl_ty, impl_len) => {
+                    self.types_may_unify(obl_ty, impl_ty)
+                        && self.consts_may_unify(obl_len, impl_len)
+                }
+                _ => false,
+            },
+            ty::Tuple(obl) => match k {
+                ty::Tuple(imp) => {
+                    obl.len() == imp.len()
+                        && iter::zip(obl.iter(), imp.iter())
+                            .all(|(obl, imp)| self.types_may_unify(obl, imp))
+                }
+                _ => false,
+            },
+            ty::RawPtr(obl_ty, obl_mutbl) => match k {
+                ty::RawPtr(imp_ty, imp_mutbl) => {
+                    obl_mutbl == imp_mutbl && self.types_may_unify(obl_ty, imp_ty)
+                }
+                _ => false,
+            },
+            ty::Dynamic(obl_preds, ..) => {
+                // Ideally we would walk the existential predicates here or at least
+                // compare their length. But considering that the relevant `Relate` impl
+                // actually sorts and deduplicates these, that doesn't work.
+                matches!(k, ty::Dynamic(impl_preds, ..) if
+                    obl_preds.principal_def_id() == impl_preds.principal_def_id()
+                )
+            }
+            ty::FnPtr(obl_sig) => match k {
+                ty::FnPtr(impl_sig) => {
+                    let ty::FnSig { inputs_and_output, c_variadic, safety, abi } =
+                        obl_sig.skip_binder();
+                    let impl_sig = impl_sig.skip_binder();
+
+                    abi == impl_sig.abi
+                        && c_variadic == impl_sig.c_variadic
+                        && safety == impl_sig.safety
+                        && inputs_and_output.len() == impl_sig.inputs_and_output.len()
+                        && iter::zip(inputs_and_output.iter(), impl_sig.inputs_and_output.iter())
+                            .all(|(obl, imp)| self.types_may_unify(obl, imp))
+                }
+                _ => false,
+            },
+
+            // Impls cannot contain these types as these cannot be named directly.
+            ty::FnDef(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => false,
+
+            // Placeholder types don't unify with anything on their own
+            ty::Placeholder(..) | ty::Bound(..) => false,
+
+            // Depending on the value of `treat_obligation_params`, we either
+            // treat generic parameters like placeholders or like inference variables.
+            ty::Param(_) => match self.treat_obligation_params {
+                TreatParams::ForLookup => false,
+                TreatParams::AsCandidateKey => true,
+            },
+
+            ty::Infer(ty::IntVar(_)) => impl_ty.is_integral(),
+
+            ty::Infer(ty::FloatVar(_)) => impl_ty.is_floating_point(),
+
+            ty::Infer(_) => true,
+
+            // As we're walking the whole type, it may encounter projections
+            // inside of binders and what not, so we're just going to assume that
+            // projections can unify with other stuff.
+            //
+            // Looking forward to lazy normalization this is the safer strategy anyways.
+            ty::Alias(..) => true,
+
+            ty::Error(_) => true,
+
+            ty::CoroutineWitness(..) => {
+                panic!("unexpected obligation type: {:?}", obligation_ty)
+            }
+        }
+    }
+
+    pub fn consts_may_unify(self, obligation_ct: I::Const, impl_ct: I::Const) -> bool {
+        let impl_val = match impl_ct.kind() {
+            ty::ConstKind::Expr(_)
+            | ty::ConstKind::Param(_)
+            | ty::ConstKind::Unevaluated(_)
+            | ty::ConstKind::Error(_) => {
+                return true;
+            }
+            ty::ConstKind::Value(_, impl_val) => impl_val,
+            ty::ConstKind::Infer(_) | ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => {
+                panic!("unexpected impl arg: {:?}", impl_ct)
+            }
+        };
+
+        match obligation_ct.kind() {
+            ty::ConstKind::Param(_) => match self.treat_obligation_params {
+                TreatParams::ForLookup => false,
+                TreatParams::AsCandidateKey => true,
+            },
+
+            // Placeholder consts don't unify with anything on their own
+            ty::ConstKind::Placeholder(_) => false,
+
+            // As we don't necessarily eagerly evaluate constants,
+            // they might unify with any value.
+            ty::ConstKind::Expr(_) | ty::ConstKind::Unevaluated(_) | ty::ConstKind::Error(_) => {
+                true
+            }
+            ty::ConstKind::Value(_, obl_val) => obl_val == impl_val,
+
+            ty::ConstKind::Infer(_) => true,
+
+            ty::ConstKind::Bound(..) => {
+                panic!("unexpected obl const: {:?}", obligation_ct)
+            }
+        }
+    }
+}
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
new file mode 100644
index 00000000000..1d0b2345b80
--- /dev/null
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -0,0 +1,98 @@
+use crate::fold::TypeFoldable;
+use crate::relate::Relate;
+use crate::solve::{Goal, NoSolution, SolverMode};
+use crate::{self as ty, Interner};
+
+pub trait InferCtxtLike: Sized {
+    type Interner: Interner;
+    fn cx(&self) -> Self::Interner;
+
+    fn solver_mode(&self) -> SolverMode;
+
+    fn universe(&self) -> ty::UniverseIndex;
+    fn create_next_universe(&self) -> ty::UniverseIndex;
+
+    fn universe_of_ty(&self, ty: ty::TyVid) -> Option<ty::UniverseIndex>;
+    fn universe_of_lt(&self, lt: ty::RegionVid) -> Option<ty::UniverseIndex>;
+    fn universe_of_ct(&self, ct: ty::ConstVid) -> Option<ty::UniverseIndex>;
+
+    fn root_ty_var(&self, var: ty::TyVid) -> ty::TyVid;
+    fn root_const_var(&self, var: ty::ConstVid) -> ty::ConstVid;
+
+    fn opportunistic_resolve_ty_var(&self, vid: ty::TyVid) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_int_var(&self, vid: ty::IntVid) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_float_var(
+        &self,
+        vid: ty::FloatVid,
+    ) -> <Self::Interner as Interner>::Ty;
+    fn opportunistic_resolve_ct_var(
+        &self,
+        vid: ty::ConstVid,
+    ) -> <Self::Interner as Interner>::Const;
+    fn opportunistic_resolve_effect_var(
+        &self,
+        vid: ty::EffectVid,
+    ) -> <Self::Interner as Interner>::Const;
+    fn opportunistic_resolve_lt_var(
+        &self,
+        vid: ty::RegionVid,
+    ) -> <Self::Interner as Interner>::Region;
+
+    fn defining_opaque_types(&self) -> <Self::Interner as Interner>::DefiningOpaqueTypes;
+
+    fn next_ty_infer(&self) -> <Self::Interner as Interner>::Ty;
+    fn next_const_infer(&self) -> <Self::Interner as Interner>::Const;
+    fn fresh_args_for_item(
+        &self,
+        def_id: <Self::Interner as Interner>::DefId,
+    ) -> <Self::Interner as Interner>::GenericArgs;
+
+    fn instantiate_binder_with_infer<T: TypeFoldable<Self::Interner> + Copy>(
+        &self,
+        value: ty::Binder<Self::Interner, T>,
+    ) -> T;
+
+    fn enter_forall<T: TypeFoldable<Self::Interner> + Copy, U>(
+        &self,
+        value: ty::Binder<Self::Interner, T>,
+        f: impl FnOnce(T) -> U,
+    ) -> U;
+
+    fn relate<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        variance: ty::Variance,
+        rhs: T,
+    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+
+    fn eq_structurally_relating_aliases<T: Relate<Self::Interner>>(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        lhs: T,
+        rhs: T,
+    ) -> Result<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>, NoSolution>;
+
+    fn shallow_resolve(
+        &self,
+        ty: <Self::Interner as Interner>::Ty,
+    ) -> <Self::Interner as Interner>::Ty;
+
+    fn resolve_vars_if_possible<T>(&self, value: T) -> T
+    where
+        T: TypeFoldable<Self::Interner>;
+
+    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T;
+
+    fn sub_regions(
+        &self,
+        sub: <Self::Interner as Interner>::Region,
+        sup: <Self::Interner as Interner>::Region,
+    );
+
+    fn register_ty_outlives(
+        &self,
+        ty: <Self::Interner as Interner>::Ty,
+        r: <Self::Interner as Interner>::Region,
+    );
+}
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index a4e1a97d505..de86a8536f7 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -9,6 +9,7 @@ use std::hash::Hash;
 use rustc_ast_ir::Mutability;
 
 use crate::data_structures::HashSet;
+use crate::elaborate::Elaboratable;
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
 use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal};
@@ -40,6 +41,10 @@ pub trait Ty<I: Interner<Ty = Self>>:
 
     fn new_var(interner: I, var: ty::TyVid) -> Self;
 
+    fn new_param(interner: I, param: I::ParamTy) -> Self;
+
+    fn new_placeholder(interner: I, param: I::PlaceholderTy) -> Self;
+
     fn new_bound(interner: I, debruijn: ty::DebruijnIndex, var: I::BoundTy) -> Self;
 
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
@@ -120,6 +125,14 @@ pub trait Ty<I: Interner<Ty = Self>>:
         matches!(self.kind(), ty::Infer(ty::TyVar(_)))
     }
 
+    fn is_floating_point(self) -> bool {
+        matches!(self.kind(), ty::Float(_) | ty::Infer(ty::FloatVar(_)))
+    }
+
+    fn is_integral(self) -> bool {
+        matches!(self.kind(), ty::Infer(ty::IntVar(_)) | ty::Int(_) | ty::Uint(_))
+    }
+
     fn is_fn_ptr(self) -> bool {
         matches!(self.kind(), ty::FnPtr(_))
     }
@@ -224,6 +237,10 @@ pub trait Region<I: Interner<Region = Self>>:
     fn new_anon_bound(interner: I, debruijn: ty::DebruijnIndex, var: ty::BoundVar) -> Self;
 
     fn new_static(interner: I) -> Self;
+
+    fn is_bound(self) -> bool {
+        matches!(self.kind(), ty::ReBound(..))
+    }
 }
 
 pub trait Const<I: Interner<Const = Self>>:
@@ -264,6 +281,10 @@ pub trait Const<I: Interner<Const = Self>>:
     }
 }
 
+pub trait ExprConst<I: Interner<ExprConst = Self>>: Copy + Debug + Hash + Eq + Relate<I> {
+    fn args(self) -> I::GenericArgs;
+}
+
 pub trait GenericsOf<I: Interner<GenericsOf = Self>> {
     fn count(&self) -> usize;
 }
@@ -413,6 +434,8 @@ pub trait Predicate<I: Interner<Predicate = Self>>:
     + UpcastFrom<I, ty::OutlivesPredicate<I, I::Region>>
     + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>>
 {
+    fn as_clause(self) -> Option<I::Clause>;
+
     fn is_coinductive(self, interner: I) -> bool;
 
     // FIXME: Eventually uplift the impl out of rustc and make this defaulted.
@@ -425,35 +448,35 @@ pub trait Clause<I: Interner<Clause = Self>>:
     + Hash
     + Eq
     + TypeFoldable<I>
-    // FIXME: Remove these, uplift the `Upcast` impls.
+    + UpcastFrom<I, ty::Binder<I, ty::ClauseKind<I>>>
     + UpcastFrom<I, ty::TraitRef<I>>
     + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
     + UpcastFrom<I, ty::ProjectionPredicate<I>>
     + UpcastFrom<I, ty::Binder<I, ty::ProjectionPredicate<I>>>
     + IntoKind<Kind = ty::Binder<I, ty::ClauseKind<I>>>
+    + Elaboratable<I>
 {
     fn as_trait_clause(self) -> Option<ty::Binder<I, ty::TraitPredicate<I>>> {
         self.kind()
-            .map_bound(|clause| {
-                if let ty::ClauseKind::Trait(t) = clause {
-                    Some(t)
-                } else {
-                    None
-                }
-            })
+            .map_bound(|clause| if let ty::ClauseKind::Trait(t) = clause { Some(t) } else { None })
             .transpose()
     }
+
     fn as_projection_clause(self) -> Option<ty::Binder<I, ty::ProjectionPredicate<I>>> {
         self.kind()
-            .map_bound(|clause| {
-                if let ty::ClauseKind::Projection(p) = clause {
-                    Some(p)
-                } else {
-                    None
-                }
-            })
+            .map_bound(
+                |clause| {
+                    if let ty::ClauseKind::Projection(p) = clause { Some(p) } else { None }
+                },
+            )
             .transpose()
     }
+
+    /// Performs a instantiation suitable for going from a
+    /// poly-trait-ref to supertraits that must hold if that
+    /// poly-trait-ref holds. This is slightly different from a normal
+    /// instantiation in terms of what happens with bound regions.
+    fn instantiate_supertrait(self, tcx: I, trait_ref: ty::Binder<I, ty::TraitRef<I>>) -> Self;
 }
 
 /// Common capabilities of placeholder kinds
@@ -498,6 +521,8 @@ pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl IntoIterator<Item = I::Ty>>;
 
     fn sized_constraint(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
+
+    fn is_fundamental(self) -> bool;
 }
 
 pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
@@ -542,6 +567,8 @@ pub trait EvaluationCache<I: Interner> {
 }
 
 pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn is_local(self) -> bool;
+
     fn as_local(self) -> Option<I::LocalDefId>;
 }
 
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index b89ea30fc34..fdd1553d389 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -32,6 +32,7 @@ pub trait Interner:
 {
     type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
+    type Span: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + SliceLike<Item = Self::GenericArg>;
@@ -109,7 +110,7 @@ pub trait Interner:
     type ParamConst: Copy + Debug + Hash + Eq + ParamLike;
     type BoundConst: Copy + Debug + Hash + Eq + BoundVarLike<Self>;
     type ValueConst: Copy + Debug + Hash + Eq;
-    type ExprConst: Copy + Debug + Hash + Eq + Relate<Self>;
+    type ExprConst: ExprConst<Self>;
 
     // Kinds of regions
     type Region: Region<Self>;
@@ -209,11 +210,15 @@ pub trait Interner:
         def_id: Self::DefId,
     ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
 
-    // FIXME: Rename this so it's obvious it's only *immediate* super predicates.
-    fn super_predicates_of(
+    fn explicit_super_predicates_of(
         self,
         def_id: Self::DefId,
-    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
+
+    fn explicit_implied_predicates_of(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = (Self::Clause, Self::Span)>>;
 
     fn has_target_features(self, def_id: Self::DefId) -> bool;
 
@@ -221,14 +226,9 @@ pub trait Interner:
 
     fn is_lang_item(self, def_id: Self::DefId, lang_item: TraitSolverLangItem) -> bool;
 
-    fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
+    fn as_lang_item(self, def_id: Self::DefId) -> Option<TraitSolverLangItem>;
 
-    // FIXME: move `fast_reject` into `rustc_type_ir`.
-    fn args_may_unify_deep(
-        self,
-        obligation_args: Self::GenericArgs,
-        impl_args: Self::GenericArgs,
-    ) -> bool;
+    fn associated_type_def_ids(self, def_id: Self::DefId) -> impl IntoIterator<Item = Self::DefId>;
 
     fn for_each_relevant_impl(
         self,
@@ -251,14 +251,9 @@ pub trait Interner:
 
     fn trait_is_object_safe(self, trait_def_id: Self::DefId) -> bool;
 
-    fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
-
-    fn fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>;
-
-    fn async_fn_trait_kind_from_def_id(self, trait_def_id: Self::DefId) -> Option<ty::ClosureKind>;
+    fn trait_is_fundamental(self, def_id: Self::DefId) -> bool;
 
-    fn supertrait_def_ids(self, trait_def_id: Self::DefId)
-    -> impl IntoIterator<Item = Self::DefId>;
+    fn trait_may_be_implemented_via_object(self, trait_def_id: Self::DefId) -> bool;
 
     fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
 
@@ -277,6 +272,11 @@ pub trait Interner:
         param_env: Self::ParamEnv,
         placeholder: Self::PlaceholderConst,
     ) -> Self::Ty;
+
+    fn anonymize_bound_vars<T: TypeFoldable<Self>>(
+        self,
+        binder: ty::Binder<Self, T>,
+    ) -> ty::Binder<Self, T>;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs
index cf5ec1ab3fe..265a4118827 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -3,8 +3,11 @@
 pub enum TraitSolverLangItem {
     // tidy-alphabetical-start
     AsyncDestruct,
+    AsyncFn,
     AsyncFnKindHelper,
     AsyncFnKindUpvars,
+    AsyncFnMut,
+    AsyncFnOnce,
     AsyncFnOnceOutput,
     AsyncIterator,
     CallOnceFuture,
@@ -17,6 +20,14 @@ pub enum TraitSolverLangItem {
     Destruct,
     DiscriminantKind,
     DynMetadata,
+    EffectsIntersection,
+    EffectsIntersectionOutput,
+    EffectsMaybe,
+    EffectsNoRuntime,
+    EffectsRuntime,
+    Fn,
+    FnMut,
+    FnOnce,
     FnPtrTrait,
     FusedIterator,
     Future,
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index 9b8ca5efdda..b14a65fc779 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -2,7 +2,7 @@
 #![allow(rustc::usage_of_ty_tykind)]
 #![cfg_attr(
     feature = "nightly",
-    feature(associated_type_defaults, min_specialization, never_type, rustc_attrs, negative_impls)
+    feature(associated_type_defaults, never_type, rustc_attrs, negative_impls)
 )]
 #![cfg_attr(feature = "nightly", allow(internal_features))]
 // tidy-alphabetical-end
@@ -20,12 +20,15 @@ pub mod visit;
 #[cfg(feature = "nightly")]
 pub mod codec;
 pub mod data_structures;
+pub mod elaborate;
 pub mod error;
+pub mod fast_reject;
 pub mod fold;
 pub mod inherent;
 pub mod ir_print;
 pub mod lang_items;
 pub mod lift;
+pub mod outlives;
 pub mod relate;
 pub mod solve;
 
@@ -35,8 +38,10 @@ mod macros;
 mod binder;
 mod canonical;
 mod const_kind;
+mod effects;
 mod flags;
 mod generic_arg;
+mod infer_ctxt;
 mod interner;
 mod opaque_ty;
 mod predicate;
@@ -51,8 +56,10 @@ pub use canonical::*;
 #[cfg(feature = "nightly")]
 pub use codec::*;
 pub use const_kind::*;
+pub use effects::*;
 pub use flags::*;
 pub use generic_arg::*;
+pub use infer_ctxt::*;
 pub use interner::*;
 pub use opaque_ty::*;
 pub use predicate::*;
diff --git a/compiler/rustc_type_ir/src/outlives.rs b/compiler/rustc_type_ir/src/outlives.rs
new file mode 100644
index 00000000000..10b6f3355d9
--- /dev/null
+++ b/compiler/rustc_type_ir/src/outlives.rs
@@ -0,0 +1,228 @@
+//! The outlives relation `T: 'a` or `'a: 'b`. This code frequently
+//! refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that
+//! RFC for reference.
+
+use smallvec::{smallvec, SmallVec};
+
+use crate::data_structures::SsoHashSet;
+use crate::inherent::*;
+use crate::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt as _, TypeVisitor};
+use crate::{self as ty, Interner};
+
+#[derive(derivative::Derivative)]
+#[derivative(Debug(bound = ""))]
+pub enum Component<I: Interner> {
+    Region(I::Region),
+    Param(I::ParamTy),
+    Placeholder(I::PlaceholderTy),
+    UnresolvedInferenceVariable(ty::InferTy),
+
+    // Projections like `T::Foo` are tricky because a constraint like
+    // `T::Foo: 'a` can be satisfied in so many ways. There may be a
+    // where-clause that says `T::Foo: 'a`, or the defining trait may
+    // include a bound like `type Foo: 'static`, or -- in the most
+    // conservative way -- we can prove that `T: 'a` (more generally,
+    // that all components in the projection outlive `'a`). This code
+    // is not in a position to judge which is the best technique, so
+    // we just product the projection as a component and leave it to
+    // the consumer to decide (but see `EscapingProjection` below).
+    Alias(ty::AliasTy<I>),
+
+    // In the case where a projection has escaping regions -- meaning
+    // regions bound within the type itself -- we always use
+    // the most conservative rule, which requires that all components
+    // outlive the bound. So for example if we had a type like this:
+    //
+    //     for<'a> Trait1<  <T as Trait2<'a,'b>>::Foo  >
+    //                      ~~~~~~~~~~~~~~~~~~~~~~~~~
+    //
+    // then the inner projection (underlined) has an escaping region
+    // `'a`. We consider that outer trait `'c` to meet a bound if `'b`
+    // outlives `'b: 'c`, and we don't consider whether the trait
+    // declares that `Foo: 'static` etc. Therefore, we just return the
+    // free components of such a projection (in this case, `'b`).
+    //
+    // However, in the future, we may want to get smarter, and
+    // actually return a "higher-ranked projection" here. Therefore,
+    // we mark that these components are part of an escaping
+    // projection, so that implied bounds code can avoid relying on
+    // them. This gives us room to improve the regionck reasoning in
+    // the future without breaking backwards compat.
+    EscapingAlias(Vec<Component<I>>),
+}
+
+/// Push onto `out` all the things that must outlive `'a` for the condition
+/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**.
+pub fn push_outlives_components<I: Interner>(
+    tcx: I,
+    ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+) {
+    ty.visit_with(&mut OutlivesCollector { tcx, out, visited: Default::default() });
+}
+
+struct OutlivesCollector<'a, I: Interner> {
+    tcx: I,
+    out: &'a mut SmallVec<[Component<I>; 4]>,
+    visited: SsoHashSet<I::Ty>,
+}
+
+impl<I: Interner> TypeVisitor<I> for OutlivesCollector<'_, I> {
+    fn visit_ty(&mut self, ty: I::Ty) -> Self::Result {
+        if !self.visited.insert(ty) {
+            return;
+        }
+        // Descend through the types, looking for the various "base"
+        // components and collecting them into `out`. This is not written
+        // with `collect()` because of the need to sometimes skip subtrees
+        // in the `subtys` iterator (e.g., when encountering a
+        // projection).
+        match ty.kind() {
+            ty::FnDef(_, args) => {
+                // HACK(eddyb) ignore lifetimes found shallowly in `args`.
+                // This is inconsistent with `ty::Adt` (including all args)
+                // and with `ty::Closure` (ignoring all args other than
+                // upvars, of which a `ty::FnDef` doesn't have any), but
+                // consistent with previous (accidental) behavior.
+                // See https://github.com/rust-lang/rust/issues/70917
+                // for further background and discussion.
+                for child in args.iter() {
+                    match child.kind() {
+                        ty::GenericArgKind::Lifetime(_) => {}
+                        ty::GenericArgKind::Type(_) | ty::GenericArgKind::Const(_) => {
+                            child.visit_with(self);
+                        }
+                    }
+                }
+            }
+
+            ty::Closure(_, args) => {
+                args.as_closure().tupled_upvars_ty().visit_with(self);
+            }
+
+            ty::CoroutineClosure(_, args) => {
+                args.as_coroutine_closure().tupled_upvars_ty().visit_with(self);
+            }
+
+            ty::Coroutine(_, args) => {
+                args.as_coroutine().tupled_upvars_ty().visit_with(self);
+
+                // We ignore regions in the coroutine interior as we don't
+                // want these to affect region inference
+            }
+
+            // All regions are bound inside a witness, and we don't emit
+            // higher-ranked outlives components currently.
+            ty::CoroutineWitness(..) => {}
+
+            // OutlivesTypeParameterEnv -- the actual checking that `X:'a`
+            // is implied by the environment is done in regionck.
+            ty::Param(p) => {
+                self.out.push(Component::Param(p));
+            }
+
+            ty::Placeholder(p) => {
+                self.out.push(Component::Placeholder(p));
+            }
+
+            // For projections, we prefer to generate an obligation like
+            // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the
+            // regionck more ways to prove that it holds. However,
+            // regionck is not (at least currently) prepared to deal with
+            // higher-ranked regions that may appear in the
+            // trait-ref. Therefore, if we see any higher-ranked regions,
+            // we simply fallback to the most restrictive rule, which
+            // requires that `Pi: 'a` for all `i`.
+            ty::Alias(_, alias_ty) => {
+                if !alias_ty.has_escaping_bound_vars() {
+                    // best case: no escaping regions, so push the
+                    // projection and skip the subtree (thus generating no
+                    // constraints for Pi). This defers the choice between
+                    // the rules OutlivesProjectionEnv,
+                    // OutlivesProjectionTraitDef, and
+                    // OutlivesProjectionComponents to regionck.
+                    self.out.push(Component::Alias(alias_ty));
+                } else {
+                    // fallback case: hard code
+                    // OutlivesProjectionComponents. Continue walking
+                    // through and constrain Pi.
+                    let mut subcomponents = smallvec![];
+                    compute_alias_components_recursive(self.tcx, ty, &mut subcomponents);
+                    self.out.push(Component::EscapingAlias(subcomponents.into_iter().collect()));
+                }
+            }
+
+            // We assume that inference variables are fully resolved.
+            // So, if we encounter an inference variable, just record
+            // the unresolved variable as a component.
+            ty::Infer(infer_ty) => {
+                self.out.push(Component::UnresolvedInferenceVariable(infer_ty));
+            }
+
+            // Most types do not introduce any region binders, nor
+            // involve any other subtle cases, and so the WF relation
+            // simply constraints any regions referenced directly by
+            // the type and then visits the types that are lexically
+            // contained within.
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Str
+            | ty::Never
+            | ty::Error(_) => {
+                // Trivial
+            }
+
+            ty::Bound(_, _) => {
+                // FIXME: Bound vars matter here!
+            }
+
+            ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Tuple(_) => {
+                ty.super_visit_with(self);
+            }
+        }
+    }
+
+    fn visit_region(&mut self, lt: I::Region) -> Self::Result {
+        if !lt.is_bound() {
+            self.out.push(Component::Region(lt));
+        }
+    }
+}
+
+/// Collect [Component]s for *all* the args of `parent`.
+///
+/// This should not be used to get the components of `parent` itself.
+/// Use [push_outlives_components] instead.
+pub fn compute_alias_components_recursive<I: Interner>(
+    tcx: I,
+    alias_ty: I::Ty,
+    out: &mut SmallVec<[Component<I>; 4]>,
+) {
+    let ty::Alias(kind, alias_ty) = alias_ty.kind() else {
+        unreachable!("can only call `compute_alias_components_recursive` on an alias type")
+    };
+
+    let opt_variances =
+        if kind == ty::Opaque { Some(tcx.variances_of(alias_ty.def_id)) } else { None };
+
+    let mut visitor = OutlivesCollector { tcx, out, visited: Default::default() };
+
+    for (index, child) in alias_ty.args.iter().enumerate() {
+        if opt_variances.and_then(|variances| variances.get(index)) == Some(ty::Bivariant) {
+            continue;
+        }
+        child.visit_with(&mut visitor);
+    }
+}
diff --git a/compiler/stable_mir/src/compiler_interface.rs b/compiler/stable_mir/src/compiler_interface.rs
index 44dbf549c1a..5f2d9b96c73 100644
--- a/compiler/stable_mir/src/compiler_interface.rs
+++ b/compiler/stable_mir/src/compiler_interface.rs
@@ -6,6 +6,7 @@
 use std::cell::Cell;
 
 use crate::abi::{FnAbi, Layout, LayoutShape};
+use crate::crate_def::Attribute;
 use crate::mir::alloc::{AllocId, GlobalAlloc};
 use crate::mir::mono::{Instance, InstanceDef, StaticDef};
 use crate::mir::{BinOp, Body, Place, UnOp};
@@ -55,6 +56,15 @@ pub trait Context {
     /// Returns the name of given `DefId`
     fn def_name(&self, def_id: DefId, trimmed: bool) -> Symbol;
 
+    /// Return attributes with the given attribute name.
+    ///
+    /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
+    /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
+    fn get_attrs_by_path(&self, def_id: DefId, attr: &[Symbol]) -> Vec<Attribute>;
+
+    /// Get all attributes of a definition.
+    fn get_all_attrs(&self, def_id: DefId) -> Vec<Attribute>;
+
     /// Returns printable, human readable form of `Span`
     fn span_to_string(&self, span: Span) -> String;
 
diff --git a/compiler/stable_mir/src/crate_def.rs b/compiler/stable_mir/src/crate_def.rs
index 67752a5e629..d9b987c28a2 100644
--- a/compiler/stable_mir/src/crate_def.rs
+++ b/compiler/stable_mir/src/crate_def.rs
@@ -50,6 +50,21 @@ pub trait CrateDef {
         let def_id = self.def_id();
         with(|cx| cx.span_of_an_item(def_id))
     }
+
+    /// Return attributes with the given attribute name.
+    ///
+    /// Single segmented name like `#[inline]` is specified as `&["inline".to_string()]`.
+    /// Multi-segmented name like `#[rustfmt::skip]` is specified as `&["rustfmt".to_string(), "skip".to_string()]`.
+    fn attrs_by_path(&self, attr: &[Symbol]) -> Vec<Attribute> {
+        let def_id = self.def_id();
+        with(|cx| cx.get_attrs_by_path(def_id, attr))
+    }
+
+    /// Return all attributes of this definition.
+    fn all_attrs(&self) -> Vec<Attribute> {
+        let def_id = self.def_id();
+        with(|cx| cx.get_all_attrs(def_id))
+    }
 }
 
 /// A trait that can be used to retrieve a definition's type.
@@ -69,6 +84,28 @@ pub trait CrateDefType: CrateDef {
     }
 }
 
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct Attribute {
+    value: String,
+    span: Span,
+}
+
+impl Attribute {
+    pub fn new(value: String, span: Span) -> Attribute {
+        Attribute { value, span }
+    }
+
+    /// Get the span of this attribute.
+    pub fn span(&self) -> Span {
+        self.span
+    }
+
+    /// Get the string representation of this attribute.
+    pub fn as_str(&self) -> &str {
+        &self.value
+    }
+}
+
 macro_rules! crate_def {
     ( $(#[$attr:meta])*
       $vis:vis $name:ident $(;)?