about summary refs log tree commit diff
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2024-06-19 05:02:35 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2024-06-19 05:02:35 +0000
commit3e93254d5e2efb3d2dd738833142de9274c7b191 (patch)
tree6ad18502b12564bac17108d771d900947abbefcb
parentd9f1d557860138da64d52c0f536db395a99b9a4a (diff)
parent2a6a42329f79d3c2a42f23ca9fb68aba511a2bc1 (diff)
downloadrust-3e93254d5e2efb3d2dd738833142de9274c7b191.tar.gz
rust-3e93254d5e2efb3d2dd738833142de9274c7b191.zip
Merge from rustc
-rw-r--r--Cargo.lock9
-rw-r--r--compiler/rustc_ast/src/ast.rs5
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs12
-rw-r--r--compiler/rustc_ast/src/util/classify.rs6
-rw-r--r--compiler/rustc_ast/src/visit.rs21
-rw-r--r--compiler/rustc_ast_lowering/messages.ftl2
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs143
-rw-r--r--compiler/rustc_ast_passes/messages.ftl5
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs207
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs17
-rw-r--r--compiler/rustc_ast_passes/src/show_span.rs5
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs20
-rw-r--r--compiler/rustc_attr/src/builtin.rs2
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs7
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs146
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs234
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs47
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs1
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs10
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/cmdline_attrs.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs6
-rw-r--r--compiler/rustc_builtin_macros/src/proc_macro_harness.rs5
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs3
-rw-r--r--compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs3
-rw-r--r--compiler/rustc_codegen_gcc/src/back/lto.rs11
-rw-r--r--compiler/rustc_codegen_gcc/src/back/write.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/errors.rs6
-rw-r--r--compiler/rustc_codegen_gcc/src/lib.rs21
-rw-r--r--compiler/rustc_codegen_llvm/src/back/lto.rs37
-rw-r--r--compiler/rustc_codegen_llvm/src/back/write.rs20
-rw-r--r--compiler/rustc_codegen_llvm/src/errors.rs10
-rw-r--r--compiler/rustc_codegen_llvm/src/lib.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs19
-rw-r--r--compiler/rustc_codegen_ssa/src/errors.rs6
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/write.rs8
-rw-r--r--compiler/rustc_const_eval/src/check_consts/mod.rs4
-rw-r--r--compiler/rustc_const_eval/src/check_consts/ops.rs2
-rw-r--r--compiler/rustc_const_eval/src/errors.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs4
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs3
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs24
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs6
-rw-r--r--compiler/rustc_errors/src/emitter.rs2
-rw-r--r--compiler/rustc_errors/src/json/tests.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs130
-rw-r--r--compiler/rustc_expand/src/base.rs8
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/macro_check.rs10
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs4
-rw-r--r--compiler/rustc_expand/src/mbe/metavar_expr.rs29
-rw-r--r--compiler/rustc_expand/src/mbe/transcribe.rs14
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs2
-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.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs7
-rw-r--r--compiler/rustc_hir_analysis/src/check/errs.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs74
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs3
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs16
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs98
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs129
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs12
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs29
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs156
-rw-r--r--compiler/rustc_interface/src/interface.rs12
-rw-r--r--compiler/rustc_interface/src/tests.rs4
-rw-r--r--compiler/rustc_lint/src/impl_trait_overcaptures.rs18
-rw-r--r--compiler/rustc_lint/src/lints.rs6
-rw-r--r--compiler/rustc_lint/src/unused.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic.rs2
-rw-r--r--compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs2
-rw-r--r--compiler/rustc_metadata/src/creader.rs6
-rw-r--r--compiler/rustc_metadata/src/errors.rs8
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/macros.rs8
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs2
-rw-r--r--compiler/rustc_middle/src/mir/interpret/allocation.rs7
-rw-r--r--compiler/rustc_middle/src/traits/solve.rs14
-rw-r--r--compiler/rustc_middle/src/traits/solve/cache.rs28
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs10
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs6
-rw-r--r--compiler/rustc_middle/src/ty/context.rs370
-rw-r--r--compiler/rustc_middle/src/ty/generic_args.rs18
-rw-r--r--compiler/rustc_middle/src/ty/generics.rs4
-rw-r--r--compiler/rustc_middle/src/ty/layout.rs4
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs10
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs42
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs16
-rw-r--r--compiler/rustc_middle/src/ty/util.rs36
-rw-r--r--compiler/rustc_mir_build/src/errors.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mappings.rs33
-rw-r--r--compiler/rustc_mir_transform/src/coverage/mod.rs8
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs2
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs4
-rw-r--r--compiler/rustc_next_trait_solver/Cargo.toml18
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs11
-rw-r--r--compiler/rustc_next_trait_solver/src/infcx.rs205
-rw-r--r--compiler/rustc_next_trait_solver/src/lib.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/resolve.rs11
-rw-r--r--compiler/rustc_next_trait_solver/src/solve.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/alias_relate.rs (renamed from compiler/rustc_trait_selection/src/solve/alias_relate.rs)20
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly/mod.rs)515
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs (renamed from compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs)24
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs)237
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs)406
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs (renamed from compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs)25
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/build.rs (renamed from compiler/rustc_trait_selection/src/solve/inspect/build.rs)43
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs6
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/mod.rs)144
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs)19
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs)27
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs)524
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs)69
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs)24
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/project_goals.rs (renamed from compiler/rustc_trait_selection/src/solve/project_goals.rs)19
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/search_graph.rs (renamed from compiler/rustc_trait_selection/src/solve/search_graph.rs)120
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs (renamed from compiler/rustc_trait_selection/src/solve/trait_goals.rs)435
-rw-r--r--compiler/rustc_parse/src/errors.rs6
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs13
-rw-r--r--compiler/rustc_parse/src/lexer/tokentrees.rs4
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs4
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs2
-rw-r--r--compiler/rustc_parse/src/lib.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr_wrapper.rs2
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs17
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs15
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs41
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs2
-rw-r--r--compiler/rustc_parse/src/parser/tests.rs6
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs41
-rw-r--r--compiler/rustc_parse/src/validate_attr.rs10
-rw-r--r--compiler/rustc_passes/src/check_attr.rs6
-rw-r--r--compiler/rustc_passes/src/errors.rs14
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs4
-rw-r--r--compiler/rustc_query_system/src/query/job.rs4
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs76
-rw-r--r--compiler/rustc_resolve/src/imports.rs4
-rw-r--r--compiler/rustc_resolve/src/late.rs2
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs28
-rw-r--r--compiler/rustc_session/src/config.rs9
-rw-r--r--compiler/rustc_session/src/errors.rs8
-rw-r--r--compiler/rustc_session/src/options.rs4
-rw-r--r--compiler/rustc_session/src/parse.rs28
-rw-r--r--compiler/rustc_session/src/session.rs31
-rw-r--r--compiler/rustc_span/src/span_encoding.rs41
-rw-r--r--compiler/rustc_symbol_mangling/src/errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve.rs12
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs18
-rw-r--r--compiler/rustc_trait_selection/src/solve/infcx.rs435
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect.rs4
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/analyse.rs47
-rw-r--r--compiler/rustc_trait_selection/src/solve/inspect/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs2
-rw-r--r--compiler/rustc_type_ir/src/infcx.rs77
-rw-r--r--compiler/rustc_type_ir/src/inherent.rs211
-rw-r--r--compiler/rustc_type_ir/src/interner.rs138
-rw-r--r--compiler/rustc_type_ir/src/lang_items.rs32
-rw-r--r--compiler/rustc_type_ir/src/lib.rs2
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs8
-rw-r--r--compiler/rustc_type_ir/src/solve.rs38
-rw-r--r--library/core/src/panic.rs2
-rw-r--r--library/core/src/panic/panic_info.rs75
-rw-r--r--library/std/src/panicking.rs13
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/config.rs16
-rw-r--r--src/librustdoc/core.rs4
-rw-r--r--src/librustdoc/doctest.rs7
-rw-r--r--src/librustdoc/doctest/make.rs2
-rw-r--r--src/librustdoc/externalfiles.rs7
-rw-r--r--src/librustdoc/lib.rs13
-rw-r--r--src/librustdoc/scrape_examples.rs5
-rw-r--r--src/librustdoc/theme.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs5
-rw-r--r--src/tools/clippy/src/driver.rs4
-rw-r--r--src/tools/miri/src/diagnostics.rs2
-rw-r--r--src/tools/run-make-support/src/lib.rs31
-rw-r--r--src/tools/run-make-support/src/llvm.rs30
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/lazy_static.rs6
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs6
-rw-r--r--src/tools/rustfmt/src/parse/session.rs8
-rw-r--r--src/tools/rustfmt/src/spanned.rs1
-rw-r--r--src/tools/rustfmt/src/types.rs15
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt15
-rw-r--r--tests/coverage/attr/nested.cov-map100
-rw-r--r--tests/coverage/attr/nested.coverage111
-rw-r--r--tests/coverage/attr/nested.rs110
-rw-r--r--tests/coverage/attr/off-on-sandwich.cov-map32
-rw-r--r--tests/coverage/attr/off-on-sandwich.coverage58
-rw-r--r--tests/coverage/attr/off-on-sandwich.rs57
-rw-r--r--tests/coverage/branch/no-mir-spans.cov-map52
-rw-r--r--tests/coverage/branch/no-mir-spans.coverage77
-rw-r--r--tests/coverage/branch/no-mir-spans.rs62
-rw-r--r--tests/run-make/allocator-shim-circular-deps/Makefile12
-rw-r--r--tests/run-make/allocator-shim-circular-deps/rmake.rs16
-rw-r--r--tests/run-make/error-found-staticlib-instead-crate/Makefile5
-rw-r--r--tests/run-make/error-found-staticlib-instead-crate/rmake.rs11
-rw-r--r--tests/run-make/extern-flag-fun/Makefile20
-rw-r--r--tests/run-make/extern-flag-fun/rmake.rs38
-rw-r--r--tests/run-make/incremental-debugger-visualizer/Makefile49
-rw-r--r--tests/run-make/incremental-debugger-visualizer/rmake.rs56
-rw-r--r--tests/run-make/incremental-session-fail/Makefile14
-rw-r--r--tests/run-make/incremental-session-fail/rmake.rs15
-rw-r--r--tests/run-make/invalid-staticlib/Makefile5
-rw-r--r--tests/run-make/invalid-staticlib/rmake.rs17
-rw-r--r--tests/run-make/issue-64153/Makefile26
-rw-r--r--tests/run-make/lto-avoid-object-duplication/downstream.rs (renamed from tests/run-make/issue-64153/downstream.rs)0
-rw-r--r--tests/run-make/lto-avoid-object-duplication/rmake.rs40
-rw-r--r--tests/run-make/lto-avoid-object-duplication/upstream.rs (renamed from tests/run-make/issue-64153/upstream.rs)0
-rw-r--r--tests/run-make/native-link-modifier-verbatim-linker/Makefile15
-rw-r--r--tests/run-make/native-link-modifier-verbatim-linker/rmake.rs41
-rw-r--r--tests/run-make/native-link-modifier-verbatim-rustc/Makefile12
-rw-r--r--tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs47
-rw-r--r--tests/run-make/no-builtins-lto/Makefile9
-rw-r--r--tests/run-make/no-builtins-lto/rmake.rs20
-rw-r--r--tests/run-make/output-filename-conflicts-with-directory/Makefile7
-rw-r--r--tests/run-make/output-filename-conflicts-with-directory/rmake.rs14
-rw-r--r--tests/run-make/output-filename-overwrites-input/Makefile14
-rw-r--r--tests/run-make/output-filename-overwrites-input/rmake.rs21
-rw-r--r--tests/run-make/separate-link-fail/Makefile7
-rw-r--r--tests/run-make/separate-link-fail/foo.rs1
-rw-r--r--tests/run-make/separate-link-fail/rmake.rs15
-rw-r--r--tests/run-make/separate-link/Makefile7
-rw-r--r--tests/run-make/separate-link/rmake.rs14
-rw-r--r--tests/run-make/used/Makefile7
-rw-r--r--tests/run-make/used/rmake.rs15
-rw-r--r--tests/ui-fulldeps/internal-lints/diagnostics.rs14
-rw-r--r--tests/ui/array-slice-vec/vec-res-add.stderr5
-rw-r--r--tests/ui/autoderef-full-lval.stderr12
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.rs16
-rw-r--r--tests/ui/binop/binary-op-not-allowed-issue-125631.stderr75
-rw-r--r--tests/ui/binop/binop-bitxor-str.stderr5
-rw-r--r--tests/ui/coverage-attr/bad-syntax.rs58
-rw-r--r--tests/ui/coverage-attr/bad-syntax.stderr78
-rw-r--r--tests/ui/coverage-attr/no-coverage.rs (renamed from tests/ui/lint/no-coverage.rs)0
-rw-r--r--tests/ui/coverage-attr/no-coverage.stderr (renamed from tests/ui/lint/no-coverage.stderr)0
-rw-r--r--tests/ui/error-codes/E0067.stderr6
-rw-r--r--tests/ui/feature-gates/feature-gate-precise-capturing.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-precise-capturing.stderr6
-rw-r--r--tests/ui/impl-trait/call_method_ambiguous.next.stderr2
-rw-r--r--tests/ui/impl-trait/call_method_ambiguous.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/apit.rs5
-rw-r--r--tests/ui/impl-trait/precise-capturing/apit.stderr19
-rw-r--r--tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs7
-rw-r--r--tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr39
-rw-r--r--tests/ui/impl-trait/precise-capturing/bad-params.rs9
-rw-r--r--tests/ui/impl-trait/precise-capturing/bad-params.stderr37
-rw-r--r--tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs9
-rw-r--r--tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr23
-rw-r--r--tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr8
-rw-r--r--tests/ui/impl-trait/precise-capturing/duplicated-use.rs10
-rw-r--r--tests/ui/impl-trait/precise-capturing/dyn-use.rs4
-rw-r--r--tests/ui/impl-trait/precise-capturing/dyn-use.stderr8
-rw-r--r--tests/ui/impl-trait/precise-capturing/elided.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/elided.stderr11
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr17
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs5
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr27
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs5
-rw-r--r--tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr23
-rw-r--r--tests/ui/impl-trait/precise-capturing/higher-ranked.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/higher-ranked.stderr11
-rw-r--r--tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr57
-rw-r--r--tests/ui/impl-trait/precise-capturing/illegal-positions.rs27
-rw-r--r--tests/ui/impl-trait/precise-capturing/ordering.rs9
-rw-r--r--tests/ui/impl-trait/precise-capturing/ordering.stderr39
-rw-r--r--tests/ui/impl-trait/precise-capturing/outlives.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/outlives.stderr11
-rw-r--r--tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed8
-rw-r--r--tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr16
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.rs9
-rw-r--r--tests/ui/impl-trait/precise-capturing/redundant.stderr51
-rw-r--r--tests/ui/impl-trait/precise-capturing/self-capture.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/self-capture.stderr11
-rw-r--r--tests/ui/impl-trait/precise-capturing/unexpected-token.rs3
-rw-r--r--tests/ui/impl-trait/precise-capturing/unexpected-token.stderr16
-rw-r--r--tests/ui/instrument-coverage/coverage-options.bad.stderr2
-rw-r--r--tests/ui/issues/issue-14915.stderr6
-rw-r--r--tests/ui/issues/issue-66706.rs4
-rw-r--r--tests/ui/issues/issue-66706.stderr8
-rw-r--r--tests/ui/minus-string.stderr5
-rw-r--r--tests/ui/parser/trait-object-delimiters.rs2
-rw-r--r--tests/ui/parser/trait-object-delimiters.stderr4
-rw-r--r--tests/ui/pattern/pattern-tyvar-2.stderr5
-rw-r--r--tests/ui/typeck/assign-non-lval-derefmut.stderr8
305 files changed, 5818 insertions, 3626 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2e10b4c49ab..14ee031ad04 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4520,7 +4520,16 @@ dependencies = [
 name = "rustc_next_trait_solver"
 version = "0.0.0"
 dependencies = [
+ "bitflags 2.5.0",
+ "derivative",
+ "rustc_ast_ir",
+ "rustc_data_structures",
+ "rustc_index",
+ "rustc_macros",
+ "rustc_serialize",
  "rustc_type_ir",
+ "rustc_type_ir_macros",
+ "tracing",
 ]
 
 [[package]]
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 9cb193b4a67..fe888d2004f 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -307,6 +307,8 @@ impl TraitBoundModifiers {
 pub enum GenericBound {
     Trait(PolyTraitRef, TraitBoundModifiers),
     Outlives(Lifetime),
+    /// Precise capturing syntax: `impl Sized + use<'a>`
+    Use(ThinVec<PreciseCapturingArg>, Span),
 }
 
 impl GenericBound {
@@ -314,6 +316,7 @@ impl GenericBound {
         match self {
             GenericBound::Trait(t, ..) => t.span,
             GenericBound::Outlives(l) => l.ident.span,
+            GenericBound::Use(_, span) => *span,
         }
     }
 }
@@ -2162,7 +2165,7 @@ pub enum TyKind {
     /// The `NodeId` exists to prevent lowering from having to
     /// generate `NodeId`s on the fly, which would complicate
     /// the generation of opaque `type Foo = impl Trait` items significantly.
-    ImplTrait(NodeId, GenericBounds, Option<P<(ThinVec<PreciseCapturingArg>, Span)>>),
+    ImplTrait(NodeId, GenericBounds),
     /// No-op; kept solely so that we can pretty-print faithfully.
     Paren(P<Ty>),
     /// Unused for now.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index cc33ce2cb56..182ad7359af 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -523,14 +523,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
         TyKind::TraitObject(bounds, _syntax) => {
             visit_vec(bounds, |bound| vis.visit_param_bound(bound))
         }
-        TyKind::ImplTrait(id, bounds, precise_capturing) => {
+        TyKind::ImplTrait(id, bounds) => {
             vis.visit_id(id);
             visit_vec(bounds, |bound| vis.visit_param_bound(bound));
-            if let Some((precise_capturing, _span)) = precise_capturing.as_deref_mut() {
-                for arg in precise_capturing {
-                    vis.visit_precise_capturing_arg(arg);
-                }
-            }
         }
         TyKind::MacCall(mac) => vis.visit_mac_call(mac),
         TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
@@ -923,6 +918,11 @@ fn noop_visit_param_bound<T: MutVisitor>(pb: &mut GenericBound, vis: &mut T) {
     match pb {
         GenericBound::Trait(ty, _modifier) => vis.visit_poly_trait_ref(ty),
         GenericBound::Outlives(lifetime) => noop_visit_lifetime(lifetime, vis),
+        GenericBound::Use(args, _) => {
+            for arg in args {
+                vis.visit_precise_capturing_arg(arg);
+            }
+        }
     }
 }
 
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 382c903625f..4b2544ac47e 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -184,7 +184,7 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                 None => break None,
             },
 
-            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds, _) => {
+            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
                 match bounds.last() {
                     Some(ast::GenericBound::Trait(bound, _)) => {
                         match path_return_type(&bound.trait_ref.path) {
@@ -192,7 +192,9 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
                             None => break None,
                         }
                     }
-                    Some(ast::GenericBound::Outlives(_)) | None => break None,
+                    Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
+                        break None;
+                    }
                 }
             }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index fa97c8db326..104a401cd53 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -52,6 +52,16 @@ pub enum BoundKind {
     /// E.g., `trait A: B`
     SuperTraits,
 }
+impl BoundKind {
+    pub fn descr(self) -> &'static str {
+        match self {
+            BoundKind::Bound => "bounds",
+            BoundKind::Impl => "`impl Trait`",
+            BoundKind::TraitObject => "`dyn` trait object bounds",
+            BoundKind::SuperTraits => "supertrait bounds",
+        }
+    }
+}
 
 #[derive(Copy, Clone, Debug)]
 pub enum FnKind<'a> {
@@ -497,13 +507,8 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
         TyKind::TraitObject(bounds, ..) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
         }
-        TyKind::ImplTrait(_, bounds, precise_capturing) => {
+        TyKind::ImplTrait(_, bounds) => {
             walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
-            if let Some((precise_capturing, _span)) = precise_capturing.as_deref() {
-                for arg in precise_capturing {
-                    try_visit!(visitor.visit_precise_capturing_arg(arg));
-                }
-            }
         }
         TyKind::Typeof(expression) => try_visit!(visitor.visit_anon_const(expression)),
         TyKind::Infer | TyKind::ImplicitSelf | TyKind::Dummy | TyKind::Err(_) => {}
@@ -688,6 +693,10 @@ pub fn walk_param_bound<'a, V: Visitor<'a>>(visitor: &mut V, bound: &'a GenericB
     match bound {
         GenericBound::Trait(typ, _modifier) => visitor.visit_poly_trait_ref(typ),
         GenericBound::Outlives(lifetime) => visitor.visit_lifetime(lifetime, LifetimeCtxt::Bound),
+        GenericBound::Use(args, _) => {
+            walk_list!(visitor, visit_precise_capturing_arg, args);
+            V::Result::output()
+        }
     }
 }
 
diff --git a/compiler/rustc_ast_lowering/messages.ftl b/compiler/rustc_ast_lowering/messages.ftl
index 10efe6fba65..7d81e45d314 100644
--- a/compiler/rustc_ast_lowering/messages.ftl
+++ b/compiler/rustc_ast_lowering/messages.ftl
@@ -128,7 +128,7 @@ ast_lowering_never_pattern_with_guard =
     a guard on a never pattern will never be run
     .suggestion = remove this guard
 
-ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
+ast_lowering_no_precise_captures_on_apit = `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
 
 ast_lowering_previously_used_here = previously used here
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 88f6e6c3b78..da8682d3d09 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -50,10 +50,10 @@ use rustc_data_structures::fx::FxIndexSet;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{DiagArgFromDisplay, DiagCtxt, StashKey};
-use rustc_hir as hir;
+use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
+use rustc_hir::{self as hir};
 use rustc_hir::{
     ConstArg, GenericArg, HirId, ItemLocalMap, MissingLifetimeKind, ParamName, TraitCandidate,
 };
@@ -188,7 +188,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         }
     }
 
-    pub(crate) fn dcx(&self) -> &'hir DiagCtxt {
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'hir> {
         self.tcx.dcx()
     }
 }
@@ -1384,6 +1384,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 }
                                 None
                             }
+                            // Ignore `use` syntax since that is not valid in objects.
+                            GenericBound::Use(_, span) => {
+                                this.dcx()
+                                    .span_delayed_bug(*span, "use<> not allowed in dyn types");
+                                None
+                            }
                         }));
                     let lifetime_bound =
                         lifetime_bound.unwrap_or_else(|| this.elided_dyn_bound(t.span));
@@ -1391,7 +1397,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 });
                 hir::TyKind::TraitObject(bounds, lifetime_bound, *kind)
             }
-            TyKind::ImplTrait(def_node_id, bounds, precise_capturing) => {
+            TyKind::ImplTrait(def_node_id, bounds) => {
                 let span = t.span;
                 match itctx {
                     ImplTraitContext::OpaqueTy { origin, fn_kind } => self.lower_opaque_impl_trait(
@@ -1401,12 +1407,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         bounds,
                         fn_kind,
                         itctx,
-                        precise_capturing.as_deref().map(|(args, span)| (args.as_slice(), *span)),
                     ),
                     ImplTraitContext::Universal => {
-                        if let Some(&(_, span)) = precise_capturing.as_deref() {
+                        if let Some(span) = bounds.iter().find_map(|bound| match *bound {
+                            ast::GenericBound::Use(_, span) => Some(span),
+                            _ => None,
+                        }) {
                             self.tcx.dcx().emit_err(errors::NoPreciseCapturesOnApit { span });
-                        };
+                        }
+
                         let span = t.span;
 
                         // HACK: pprust breaks strings with newlines when the type
@@ -1517,7 +1526,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         bounds: &GenericBounds,
         fn_kind: Option<FnDeclKind>,
         itctx: ImplTraitContext,
-        precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
     ) -> hir::TyKind<'hir> {
         // Make sure we know that some funky desugaring has been going on here.
         // This is a first: there is code in other places like for loop
@@ -1526,59 +1534,64 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         // frequently opened issues show.
         let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::OpaqueTy, span, None);
 
-        let captured_lifetimes_to_duplicate =
-            if let Some((precise_capturing, _)) = precise_capturing_args {
-                // We'll actually validate these later on; all we need is the list of
-                // lifetimes to duplicate during this portion of lowering.
-                precise_capturing
-                    .iter()
-                    .filter_map(|arg| match arg {
-                        PreciseCapturingArg::Lifetime(lt) => Some(*lt),
-                        PreciseCapturingArg::Arg(..) => None,
-                    })
-                    // Add in all the lifetimes mentioned in the bounds. We will error
-                    // them out later, but capturing them here is important to make sure
-                    // they actually get resolved in resolve_bound_vars.
-                    .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
-                    .collect()
-            } else {
-                match origin {
-                    hir::OpaqueTyOrigin::TyAlias { .. } => {
-                        // type alias impl trait and associated type position impl trait were
-                        // decided to capture all in-scope lifetimes, which we collect for
-                        // all opaques during resolution.
+        let captured_lifetimes_to_duplicate = if let Some(args) =
+            // We only look for one `use<...>` syntax since we syntactially reject more than one.
+            bounds.iter().find_map(
+                |bound| match bound {
+                    ast::GenericBound::Use(a, _) => Some(a),
+                    _ => None,
+                },
+            ) {
+            // We'll actually validate these later on; all we need is the list of
+            // lifetimes to duplicate during this portion of lowering.
+            args.iter()
+                .filter_map(|arg| match arg {
+                    PreciseCapturingArg::Lifetime(lt) => Some(*lt),
+                    PreciseCapturingArg::Arg(..) => None,
+                })
+                // Add in all the lifetimes mentioned in the bounds. We will error
+                // them out later, but capturing them here is important to make sure
+                // they actually get resolved in resolve_bound_vars.
+                .chain(lifetime_collector::lifetimes_in_bounds(self.resolver, bounds))
+                .collect()
+        } else {
+            match origin {
+                hir::OpaqueTyOrigin::TyAlias { .. } => {
+                    // type alias impl trait and associated type position impl trait were
+                    // decided to capture all in-scope lifetimes, which we collect for
+                    // all opaques during resolution.
+                    self.resolver
+                        .take_extra_lifetime_params(opaque_ty_node_id)
+                        .into_iter()
+                        .map(|(ident, id, _)| Lifetime { id, ident })
+                        .collect()
+                }
+                hir::OpaqueTyOrigin::FnReturn(..) => {
+                    if matches!(
+                        fn_kind.expect("expected RPITs to be lowered with a FnKind"),
+                        FnDeclKind::Impl | FnDeclKind::Trait
+                    ) || self.tcx.features().lifetime_capture_rules_2024
+                        || span.at_least_rust_2024()
+                    {
+                        // return-position impl trait in trait was decided to capture all
+                        // in-scope lifetimes, which we collect for all opaques during resolution.
                         self.resolver
                             .take_extra_lifetime_params(opaque_ty_node_id)
                             .into_iter()
                             .map(|(ident, id, _)| Lifetime { id, ident })
                             .collect()
-                    }
-                    hir::OpaqueTyOrigin::FnReturn(..) => {
-                        if matches!(
-                            fn_kind.expect("expected RPITs to be lowered with a FnKind"),
-                            FnDeclKind::Impl | FnDeclKind::Trait
-                        ) || self.tcx.features().lifetime_capture_rules_2024
-                            || span.at_least_rust_2024()
-                        {
-                            // return-position impl trait in trait was decided to capture all
-                            // in-scope lifetimes, which we collect for all opaques during resolution.
-                            self.resolver
-                                .take_extra_lifetime_params(opaque_ty_node_id)
-                                .into_iter()
-                                .map(|(ident, id, _)| Lifetime { id, ident })
-                                .collect()
-                        } else {
-                            // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
-                            // example, we only need to duplicate lifetimes that appear in the
-                            // bounds, since those are the only ones that are captured by the opaque.
-                            lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
-                        }
-                    }
-                    hir::OpaqueTyOrigin::AsyncFn(..) => {
-                        unreachable!("should be using `lower_async_fn_ret_ty`")
+                    } else {
+                        // in fn return position, like the `fn test<'a>() -> impl Debug + 'a`
+                        // example, we only need to duplicate lifetimes that appear in the
+                        // bounds, since those are the only ones that are captured by the opaque.
+                        lifetime_collector::lifetimes_in_bounds(self.resolver, bounds)
                     }
                 }
-            };
+                hir::OpaqueTyOrigin::AsyncFn(..) => {
+                    unreachable!("should be using `lower_async_fn_ret_ty`")
+                }
+            }
+        };
         debug!(?captured_lifetimes_to_duplicate);
 
         self.lower_opaque_inner(
@@ -1588,7 +1601,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             captured_lifetimes_to_duplicate,
             span,
             opaque_ty_span,
-            precise_capturing_args,
             |this| this.lower_param_bounds(bounds, itctx),
         )
     }
@@ -1601,7 +1613,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         captured_lifetimes_to_duplicate: FxIndexSet<Lifetime>,
         span: Span,
         opaque_ty_span: Span,
-        precise_capturing_args: Option<(&[PreciseCapturingArg], Span)>,
         lower_item_bounds: impl FnOnce(&mut Self) -> &'hir [hir::GenericBound<'hir>],
     ) -> hir::TyKind<'hir> {
         let opaque_ty_def_id = self.create_def(
@@ -1688,18 +1699,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // Install the remapping from old to new (if any). This makes sure that
             // any lifetimes that would have resolved to the def-id of captured
             // lifetimes are remapped to the new *synthetic* lifetimes of the opaque.
-            let (bounds, precise_capturing_args) =
-                this.with_remapping(captured_to_synthesized_mapping, |this| {
-                    (
-                        lower_item_bounds(this),
-                        precise_capturing_args.map(|(precise_capturing, span)| {
-                            (
-                                this.lower_precise_capturing_args(precise_capturing),
-                                this.lower_span(span),
-                            )
-                        }),
-                    )
-                });
+            let bounds = this
+                .with_remapping(captured_to_synthesized_mapping, |this| lower_item_bounds(this));
 
             let generic_params =
                 this.arena.alloc_from_iter(synthesized_lifetime_definitions.iter().map(
@@ -1744,7 +1745,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 origin,
                 lifetime_mapping,
                 in_trait,
-                precise_capturing_args,
             };
 
             // Generate an `type Foo = impl Trait;` declaration.
@@ -1955,7 +1955,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             captured_lifetimes,
             span,
             opaque_ty_span,
-            None,
             |this| {
                 let bound = this.lower_coroutine_fn_output_type_to_bound(
                     output,
@@ -2038,6 +2037,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             GenericBound::Outlives(lifetime) => {
                 hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
             }
+            GenericBound::Use(args, span) => hir::GenericBound::Use(
+                self.lower_precise_capturing_args(args),
+                self.lower_span(*span),
+            ),
         }
     }
 
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index 9a8689e27c0..2626631d800 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -215,6 +215,11 @@ ast_passes_pattern_in_fn_pointer = patterns aren't allowed in function pointer t
 ast_passes_pattern_in_foreign = patterns aren't allowed in foreign function declarations
     .label = pattern not allowed in foreign function
 
+ast_passes_precise_capturing_duplicated = duplicate `use<...>` precise capturing syntax
+    .label = second `use<...>` here
+
+ast_passes_precise_capturing_not_allowed_here = `use<...>` precise capturing syntax not allowed in {$loc}
+
 ast_passes_show_span = {$msg}
 
 ast_passes_stability_outside_std = stability attributes may not be used outside of the standard library
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 0fbb288cc96..b274a9b9114 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -12,6 +12,7 @@ use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor}
 use rustc_ast::*;
 use rustc_ast_pretty::pprust::{self, State};
 use rustc_data_structures::fx::FxIndexMap;
+use rustc_errors::DiagCtxtHandle;
 use rustc_feature::Features;
 use rustc_parse::validate_attr;
 use rustc_session::lint::builtin::{
@@ -193,8 +194,24 @@ impl<'a> AstValidator<'a> {
     // Mirrors `visit::walk_ty`, but tracks relevant state.
     fn walk_ty(&mut self, t: &'a Ty) {
         match &t.kind {
-            TyKind::ImplTrait(..) => {
-                self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t))
+            TyKind::ImplTrait(_, bounds) => {
+                self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t));
+
+                // FIXME(precise_capturing): If we were to allow `use` in other positions
+                // (e.g. GATs), then we must validate those as well. However, we don't have
+                // a good way of doing this with the current `Visitor` structure.
+                let mut use_bounds = bounds
+                    .iter()
+                    .filter_map(|bound| match bound {
+                        GenericBound::Use(_, span) => Some(span),
+                        _ => None,
+                    })
+                    .copied();
+                if let Some(bound1) = use_bounds.next()
+                    && let Some(bound2) = use_bounds.next()
+                {
+                    self.dcx().emit_err(errors::DuplicatePreciseCapturing { bound1, bound2 });
+                }
             }
             TyKind::TraitObject(..) => self
                 .with_tilde_const(Some(DisallowTildeConstContext::TraitObject), |this| {
@@ -253,7 +270,7 @@ impl<'a> AstValidator<'a> {
         }
     }
 
-    fn dcx(&self) -> &rustc_errors::DiagCtxt {
+    fn dcx(&self) -> DiagCtxtHandle<'a> {
         self.session.dcx()
     }
 
@@ -751,7 +768,7 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
-            TyKind::ImplTrait(_, bounds, _) => {
+            TyKind::ImplTrait(_, bounds) => {
                 if self.is_impl_trait_banned {
                     self.dcx().emit_err(errors::ImplTraitPath { span: ty.span });
                 }
@@ -793,11 +810,7 @@ impl<'a> AstValidator<'a> {
 
 /// Checks that generic parameters are in the correct order,
 /// which is lifetimes, then types and then consts. (`<'a, T, const N: usize>`)
-fn validate_generic_param_order(
-    dcx: &rustc_errors::DiagCtxt,
-    generics: &[GenericParam],
-    span: Span,
-) {
+fn validate_generic_param_order(dcx: DiagCtxtHandle<'_>, generics: &[GenericParam], span: Span) {
     let mut max_param: Option<ParamKindOrd> = None;
     let mut out_of_order = FxIndexMap::default();
     let mut param_idents = Vec::with_capacity(generics.len());
@@ -1304,6 +1317,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                                     }
                                 }
                                 GenericBound::Outlives(_) => {}
+                                GenericBound::Use(..) => {}
                             }
                         }
                     }
@@ -1322,95 +1336,110 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
-        if let GenericBound::Trait(poly, modifiers) = bound {
-            match (ctxt, modifiers.constness, modifiers.polarity) {
-                (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                    self.dcx().emit_err(errors::OptionalTraitSupertrait {
-                        span: poly.span,
-                        path_str: pprust::path_to_string(&poly.trait_ref.path),
-                    });
-                }
-                (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
-                    self.dcx().emit_err(errors::OptionalTraitObject { span: poly.span });
-                }
-                (BoundKind::TraitObject, BoundConstness::Always(_), BoundPolarity::Positive) => {
-                    self.dcx().emit_err(errors::ConstBoundTraitObject { span: poly.span });
+        match bound {
+            GenericBound::Trait(trait_ref, modifiers) => {
+                match (ctxt, modifiers.constness, modifiers.polarity) {
+                    (BoundKind::SuperTraits, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
+                        self.dcx().emit_err(errors::OptionalTraitSupertrait {
+                            span: trait_ref.span,
+                            path_str: pprust::path_to_string(&trait_ref.trait_ref.path),
+                        });
+                    }
+                    (BoundKind::TraitObject, BoundConstness::Never, BoundPolarity::Maybe(_)) => {
+                        self.dcx().emit_err(errors::OptionalTraitObject { span: trait_ref.span });
+                    }
+                    (
+                        BoundKind::TraitObject,
+                        BoundConstness::Always(_),
+                        BoundPolarity::Positive,
+                    ) => {
+                        self.dcx().emit_err(errors::ConstBoundTraitObject { span: trait_ref.span });
+                    }
+                    (_, BoundConstness::Maybe(span), BoundPolarity::Positive)
+                        if let Some(reason) = &self.disallow_tilde_const =>
+                    {
+                        let reason = match reason {
+                            DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
+                                errors::TildeConstReason::Closure
+                            }
+                            DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
+                                errors::TildeConstReason::Function { ident: ident.span }
+                            }
+                            &DisallowTildeConstContext::Trait(span) => {
+                                errors::TildeConstReason::Trait { span }
+                            }
+                            &DisallowTildeConstContext::TraitImpl(span) => {
+                                errors::TildeConstReason::TraitImpl { span }
+                            }
+                            &DisallowTildeConstContext::Impl(span) => {
+                                // FIXME(effects): Consider providing a help message or even a structured
+                                // suggestion for moving such bounds to the assoc const fns if available.
+                                errors::TildeConstReason::Impl { span }
+                            }
+                            &DisallowTildeConstContext::TraitAssocTy(span) => {
+                                errors::TildeConstReason::TraitAssocTy { span }
+                            }
+                            &DisallowTildeConstContext::TraitImplAssocTy(span) => {
+                                errors::TildeConstReason::TraitImplAssocTy { span }
+                            }
+                            &DisallowTildeConstContext::InherentAssocTy(span) => {
+                                errors::TildeConstReason::InherentAssocTy { span }
+                            }
+                            DisallowTildeConstContext::TraitObject => {
+                                errors::TildeConstReason::TraitObject
+                            }
+                            DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
+                        };
+                        self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
+                    }
+                    (
+                        _,
+                        BoundConstness::Always(_) | BoundConstness::Maybe(_),
+                        BoundPolarity::Negative(_) | BoundPolarity::Maybe(_),
+                    ) => {
+                        self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers {
+                            span: bound.span(),
+                            left: modifiers.constness.as_str(),
+                            right: modifiers.polarity.as_str(),
+                        });
+                    }
+                    _ => {}
                 }
-                (_, BoundConstness::Maybe(span), BoundPolarity::Positive)
-                    if let Some(reason) = &self.disallow_tilde_const =>
+
+                // Negative trait bounds are not allowed to have associated constraints
+                if let BoundPolarity::Negative(_) = modifiers.polarity
+                    && let Some(segment) = trait_ref.trait_ref.path.segments.last()
                 {
-                    let reason = match reason {
-                        DisallowTildeConstContext::Fn(FnKind::Closure(..)) => {
-                            errors::TildeConstReason::Closure
-                        }
-                        DisallowTildeConstContext::Fn(FnKind::Fn(_, ident, ..)) => {
-                            errors::TildeConstReason::Function { ident: ident.span }
-                        }
-                        &DisallowTildeConstContext::Trait(span) => {
-                            errors::TildeConstReason::Trait { span }
-                        }
-                        &DisallowTildeConstContext::TraitImpl(span) => {
-                            errors::TildeConstReason::TraitImpl { span }
-                        }
-                        &DisallowTildeConstContext::Impl(span) => {
-                            // FIXME(effects): Consider providing a help message or even a structured
-                            // suggestion for moving such bounds to the assoc const fns if available.
-                            errors::TildeConstReason::Impl { span }
-                        }
-                        &DisallowTildeConstContext::TraitAssocTy(span) => {
-                            errors::TildeConstReason::TraitAssocTy { span }
-                        }
-                        &DisallowTildeConstContext::TraitImplAssocTy(span) => {
-                            errors::TildeConstReason::TraitImplAssocTy { span }
-                        }
-                        &DisallowTildeConstContext::InherentAssocTy(span) => {
-                            errors::TildeConstReason::InherentAssocTy { span }
-                        }
-                        DisallowTildeConstContext::TraitObject => {
-                            errors::TildeConstReason::TraitObject
+                    match segment.args.as_deref() {
+                        Some(ast::GenericArgs::AngleBracketed(args)) => {
+                            for arg in &args.args {
+                                if let ast::AngleBracketedArg::Constraint(constraint) = arg {
+                                    self.dcx().emit_err(errors::ConstraintOnNegativeBound {
+                                        span: constraint.span,
+                                    });
+                                }
+                            }
                         }
-                        DisallowTildeConstContext::Item => errors::TildeConstReason::Item,
-                    };
-                    self.dcx().emit_err(errors::TildeConstDisallowed { span, reason });
-                }
-                (
-                    _,
-                    BoundConstness::Always(_) | BoundConstness::Maybe(_),
-                    BoundPolarity::Negative(_) | BoundPolarity::Maybe(_),
-                ) => {
-                    self.dcx().emit_err(errors::IncompatibleTraitBoundModifiers {
-                        span: bound.span(),
-                        left: modifiers.constness.as_str(),
-                        right: modifiers.polarity.as_str(),
-                    });
-                }
-                _ => {}
-            }
-        }
-
-        // Negative trait bounds are not allowed to have associated constraints
-        if let GenericBound::Trait(trait_ref, modifiers) = bound
-            && let BoundPolarity::Negative(_) = modifiers.polarity
-            && let Some(segment) = trait_ref.trait_ref.path.segments.last()
-        {
-            match segment.args.as_deref() {
-                Some(ast::GenericArgs::AngleBracketed(args)) => {
-                    for arg in &args.args {
-                        if let ast::AngleBracketedArg::Constraint(constraint) = arg {
-                            self.dcx().emit_err(errors::ConstraintOnNegativeBound {
-                                span: constraint.span,
+                        // The lowered form of parenthesized generic args contains an associated type binding.
+                        Some(ast::GenericArgs::Parenthesized(args)) => {
+                            self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
+                                span: args.span,
                             });
                         }
+                        None => {}
                     }
                 }
-                // The lowered form of parenthesized generic args contains an associated type binding.
-                Some(ast::GenericArgs::Parenthesized(args)) => {
-                    self.dcx().emit_err(errors::NegativeBoundWithParentheticalNotation {
-                        span: args.span,
+            }
+            GenericBound::Outlives(_) => {}
+            GenericBound::Use(_, span) => match ctxt {
+                BoundKind::Impl => {}
+                BoundKind::Bound | BoundKind::TraitObject | BoundKind::SuperTraits => {
+                    self.dcx().emit_err(errors::PreciseCapturingNotAllowedHere {
+                        loc: ctxt.descr(),
+                        span: *span,
                     });
                 }
-                None => {}
-            }
+            },
         }
 
         visit::walk_param_bound(self, bound)
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 260c182bd9e..601910ded20 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -844,3 +844,20 @@ pub struct MatchArmWithNoBody {
     #[suggestion(code = " => todo!(),", applicability = "has-placeholders")]
     pub suggestion: Span,
 }
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_precise_capturing_not_allowed_here)]
+pub struct PreciseCapturingNotAllowedHere {
+    #[primary_span]
+    pub span: Span,
+    pub loc: &'static str,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_precise_capturing_duplicated)]
+pub struct DuplicatePreciseCapturing {
+    #[primary_span]
+    pub bound1: Span,
+    #[label]
+    pub bound2: Span,
+}
diff --git a/compiler/rustc_ast_passes/src/show_span.rs b/compiler/rustc_ast_passes/src/show_span.rs
index 10590074282..e7ba2e7fc30 100644
--- a/compiler/rustc_ast_passes/src/show_span.rs
+++ b/compiler/rustc_ast_passes/src/show_span.rs
@@ -8,6 +8,7 @@ use std::str::FromStr;
 use rustc_ast as ast;
 use rustc_ast::visit;
 use rustc_ast::visit::Visitor;
+use rustc_errors::DiagCtxtHandle;
 
 use crate::errors;
 
@@ -31,7 +32,7 @@ impl FromStr for Mode {
 }
 
 struct ShowSpanVisitor<'a> {
-    dcx: &'a rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'a>,
     mode: Mode,
 }
 
@@ -58,7 +59,7 @@ impl<'a> Visitor<'a> for ShowSpanVisitor<'a> {
     }
 }
 
-pub fn run(dcx: &rustc_errors::DiagCtxt, mode: &str, krate: &ast::Crate) {
+pub fn run(dcx: DiagCtxtHandle<'_>, mode: &str, krate: &ast::Crate) {
     let Ok(mode) = mode.parse() else {
         return;
     };
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 4eb2a103fd8..0225c95dca8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1187,17 +1187,8 @@ impl<'a> State<'a> {
                 }
                 self.print_type_bounds(bounds);
             }
-            ast::TyKind::ImplTrait(_, bounds, precise_capturing_args) => {
+            ast::TyKind::ImplTrait(_, bounds) => {
                 self.word_nbsp("impl");
-                if let Some((precise_capturing_args, ..)) = precise_capturing_args.as_deref() {
-                    self.word("use");
-                    self.word("<");
-                    self.commasep(Inconsistent, precise_capturing_args, |s, arg| match arg {
-                        ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0),
-                        ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
-                    });
-                    self.word(">")
-                }
                 self.print_type_bounds(bounds);
             }
             ast::TyKind::Array(ty, length) => {
@@ -1800,6 +1791,15 @@ impl<'a> State<'a> {
                     self.print_poly_trait_ref(tref);
                 }
                 GenericBound::Outlives(lt) => self.print_lifetime(*lt),
+                GenericBound::Use(args, _) => {
+                    self.word("use");
+                    self.word("<");
+                    self.commasep(Inconsistent, args, |s, arg| match arg {
+                        ast::PreciseCapturingArg::Arg(p, _) => s.print_path(p, false, 0),
+                        ast::PreciseCapturingArg::Lifetime(lt) => s.print_lifetime(*lt),
+                    });
+                    self.word(">")
+                }
             }
         }
     }
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 5113c5adc8f..34c24a26f7b 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -596,7 +596,7 @@ pub fn eval_condition(
     features: Option<&Features>,
     eval: &mut impl FnMut(Condition) -> bool,
 ) -> bool {
-    let dcx = &sess.psess.dcx;
+    let dcx = sess.dcx();
     match &cfg.kind {
         ast::MetaItemKind::List(mis) if cfg.name_or_empty() == sym::version => {
             try_gate_cfg(sym::version, cfg.span, sess, features);
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
index 303909de343..0cffeed0a75 100644
--- a/compiler/rustc_attr/src/session_diagnostics.rs
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -1,7 +1,8 @@
 use std::num::IntErrorKind;
 
 use rustc_ast as ast;
-use rustc_errors::{codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::DiagCtxtHandle;
+use rustc_errors::{codes::*, Applicability, Diag, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
 
@@ -49,7 +50,7 @@ pub(crate) struct UnknownMetaItem<'a> {
 
 // Manual implementation to be able to format `expected` items correctly.
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnknownMetaItem<'_> {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let expected = self.expected.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>();
         Diag::new(dcx, level, fluent::attr_unknown_meta_item)
             .with_span(self.span)
@@ -202,7 +203,7 @@ pub(crate) struct UnsupportedLiteral {
 }
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UnsupportedLiteral {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let mut diag = Diag::new(
             dcx,
             level,
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 622feb4e4c7..c1f75328272 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -1,13 +1,13 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 
-use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxt};
+use rustc_errors::{codes::*, struct_span_code_err, Diag, DiagCtxtHandle};
 use rustc_middle::span_bug;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
 
 impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
-    pub fn dcx(&self) -> &'tcx DiagCtxt {
+    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
         self.infcx.dcx()
     }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 6bc340e44f5..197da3eb641 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -228,7 +228,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 seen_spans.insert(move_span);
             }
 
-            use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action);
+            use_spans.var_path_only_subdiag(&mut err, desired_action);
 
             if !is_loop_move {
                 err.span_label(
@@ -303,24 +303,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             if needs_note {
                 if let Some(local) = place.as_local() {
                     let span = self.body.local_decls[local].source_info.span;
-                    err.subdiagnostic(
-                        self.dcx(),
-                        crate::session_diagnostics::TypeNoCopy::Label {
-                            is_partial_move,
-                            ty,
-                            place: &note_msg,
-                            span,
-                        },
-                    );
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                        is_partial_move,
+                        ty,
+                        place: &note_msg,
+                        span,
+                    });
                 } else {
-                    err.subdiagnostic(
-                        self.dcx(),
-                        crate::session_diagnostics::TypeNoCopy::Note {
-                            is_partial_move,
-                            ty,
-                            place: &note_msg,
-                        },
-                    );
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {
+                        is_partial_move,
+                        ty,
+                        place: &note_msg,
+                    });
                 };
             }
 
@@ -597,7 +591,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             E0381,
             "{used} binding {desc}{isnt_initialized}"
         );
-        use_spans.var_path_only_subdiag(self.dcx(), &mut err, desired_action);
+        use_spans.var_path_only_subdiag(&mut err, desired_action);
 
         if let InitializationRequiringAction::PartialAssignment
         | InitializationRequiringAction::Assignment = desired_action
@@ -996,7 +990,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         err: &mut Diag<'_>,
         ty: Ty<'tcx>,
-        expr: &'cx hir::Expr<'cx>,
+        expr: &hir::Expr<'_>,
     ) {
         let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());
         let hir::ExprKind::Struct(struct_qpath, fields, Some(base)) = expr.kind else { return };
@@ -1084,8 +1078,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         err: &mut Diag<'_>,
         ty: Ty<'tcx>,
-        mut expr: &'cx hir::Expr<'cx>,
-        mut other_expr: Option<&'cx hir::Expr<'cx>>,
+        mut expr: &'tcx hir::Expr<'tcx>,
+        mut other_expr: Option<&'tcx hir::Expr<'tcx>>,
         use_spans: Option<UseSpans<'tcx>>,
     ) {
         if let hir::ExprKind::Struct(_, _, Some(_)) = expr.kind {
@@ -1410,13 +1404,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             &value_msg,
         );
 
-        borrow_spans.var_path_only_subdiag(
-            self.dcx(),
-            &mut err,
-            crate::InitializationRequiringAction::Borrow,
-        );
+        borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);
 
-        move_spans.var_subdiag(self.dcx(), &mut err, None, |kind, var_span| {
+        move_spans.var_subdiag(&mut err, None, |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             match kind {
                 hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
@@ -1468,7 +1458,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             borrow_span,
             &self.describe_any_place(borrow.borrowed_place.as_ref()),
         );
-        borrow_spans.var_subdiag(self.dcx(), &mut err, Some(borrow.kind), |kind, var_span| {
+        borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             let place = &borrow.borrowed_place;
             let desc_place = self.describe_any_place(place.as_ref());
@@ -1633,7 +1623,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         "mutably borrow",
                     );
                     borrow_spans.var_subdiag(
-                        self.dcx(),
                         &mut err,
                         Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }),
                         |kind, var_span| {
@@ -1730,64 +1719,45 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         };
 
         if issued_spans == borrow_spans {
-            borrow_spans.var_subdiag(
-                self.dcx(),
-                &mut err,
-                Some(gen_borrow_kind),
-                |kind, var_span| {
-                    use crate::session_diagnostics::CaptureVarCause::*;
-                    match kind {
-                        hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
-                            place: desc_place,
-                            var_span,
-                            is_single_var: false,
-                        },
-                        hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
-                            BorrowUsePlaceClosure {
-                                place: desc_place,
-                                var_span,
-                                is_single_var: false,
-                            }
-                        }
+            borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
+                use crate::session_diagnostics::CaptureVarCause::*;
+                match kind {
+                    hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {
+                        place: desc_place,
+                        var_span,
+                        is_single_var: false,
+                    },
+                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
+                        BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
                     }
-                },
-            );
+                }
+            });
         } else {
-            issued_spans.var_subdiag(
-                self.dcx(),
-                &mut err,
-                Some(issued_borrow.kind),
-                |kind, var_span| {
-                    use crate::session_diagnostics::CaptureVarCause::*;
-                    let borrow_place = &issued_borrow.borrowed_place;
-                    let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
-                    match kind {
-                        hir::ClosureKind::Coroutine(_) => {
-                            FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
-                        }
-                        hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
-                            FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
-                        }
+            issued_spans.var_subdiag(&mut err, Some(issued_borrow.kind), |kind, var_span| {
+                use crate::session_diagnostics::CaptureVarCause::*;
+                let borrow_place = &issued_borrow.borrowed_place;
+                let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());
+                match kind {
+                    hir::ClosureKind::Coroutine(_) => {
+                        FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
                     }
-                },
-            );
+                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
+                        FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
+                    }
+                }
+            });
 
-            borrow_spans.var_subdiag(
-                self.dcx(),
-                &mut err,
-                Some(gen_borrow_kind),
-                |kind, var_span| {
-                    use crate::session_diagnostics::CaptureVarCause::*;
-                    match kind {
-                        hir::ClosureKind::Coroutine(_) => {
-                            SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
-                        }
-                        hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
-                            SecondBorrowUsePlaceClosure { place: desc_place, var_span }
-                        }
+            borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
+                use crate::session_diagnostics::CaptureVarCause::*;
+                match kind {
+                    hir::ClosureKind::Coroutine(_) => {
+                        SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
                     }
-                },
-            );
+                    hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
+                        SecondBorrowUsePlaceClosure { place: desc_place, var_span }
+                    }
+                }
+            });
         }
 
         if union_type_name != "" {
@@ -2016,7 +1986,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         );
     }
 
-    pub(crate) fn find_expr(&self, span: Span) -> Option<&hir::Expr<'_>> {
+    pub(crate) fn find_expr(&self, span: Span) -> Option<&'tcx hir::Expr<'tcx>> {
         let tcx = self.infcx.tcx;
         let body_id = tcx.hir_node(self.mir_hir_id()).body_id()?;
         let mut expr_finder = FindExprBySpan::new(span, tcx);
@@ -2961,7 +2931,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             err.span_label(borrow_span, "borrowed value does not live long enough");
             err.span_label(drop_span, format!("`{name}` dropped here while still borrowed"));
 
-            borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| {
+            borrow_spans.args_subdiag(&mut err, |args_span| {
                 crate::session_diagnostics::CaptureArgLabel::Capture {
                     is_within: borrow_spans.for_coroutine(),
                     args_span,
@@ -3219,7 +3189,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             None,
         );
 
-        borrow_spans.args_subdiag(self.dcx(), &mut err, |args_span| {
+        borrow_spans.args_subdiag(&mut err, |args_span| {
             crate::session_diagnostics::CaptureArgLabel::Capture {
                 is_within: borrow_spans.for_coroutine(),
                 args_span,
@@ -3680,7 +3650,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     "assign",
                 );
 
-                loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| {
+                loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
                     use crate::session_diagnostics::CaptureVarCause::*;
                     match kind {
                         hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
@@ -3698,7 +3668,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
         let mut err = self.cannot_assign_to_borrowed(span, loan_span, &descr_place);
 
-        loan_spans.var_subdiag(self.dcx(), &mut err, Some(loan.kind), |kind, var_span| {
+        loan_spans.var_subdiag(&mut err, Some(loan.kind), |kind, var_span| {
             use crate::session_diagnostics::CaptureVarCause::*;
             match kind {
                 hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index abb0b5afbd8..5b4269caccb 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -4,8 +4,8 @@ use crate::session_diagnostics::{
     CaptureArgLabel, CaptureReasonLabel, CaptureReasonNote, CaptureReasonSuggest, CaptureVarCause,
     CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
 };
+use rustc_errors::MultiSpan;
 use rustc_errors::{Applicability, Diag};
-use rustc_errors::{DiagCtxt, MultiSpan};
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::CoroutineKind;
 use rustc_hir::{self as hir, LangItem};
@@ -130,16 +130,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     if let ty::Closure(did, _) = self.body.local_decls[closure].ty.kind() {
                         let did = did.expect_local();
                         if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                            diag.subdiagnostic(
-                                self.dcx(),
-                                OnClosureNote::InvokedTwice {
-                                    place_name: &ty::place_to_string_for_capture(
-                                        self.infcx.tcx,
-                                        hir_place,
-                                    ),
-                                    span: *span,
-                                },
-                            );
+                            diag.subdiagnostic(OnClosureNote::InvokedTwice {
+                                place_name: &ty::place_to_string_for_capture(
+                                    self.infcx.tcx,
+                                    hir_place,
+                                ),
+                                span: *span,
+                            });
                             return true;
                         }
                     }
@@ -152,13 +149,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             if let ty::Closure(did, _) = self.body.local_decls[target].ty.kind() {
                 let did = did.expect_local();
                 if let Some((span, hir_place)) = self.infcx.tcx.closure_kind_origin(did) {
-                    diag.subdiagnostic(
-                        self.dcx(),
-                        OnClosureNote::MovedTwice {
-                            place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
-                            span: *span,
-                        },
-                    );
+                    diag.subdiagnostic(OnClosureNote::MovedTwice {
+                        place_name: &ty::place_to_string_for_capture(self.infcx.tcx, hir_place),
+                        span: *span,
+                    });
                     return true;
                 }
             }
@@ -591,14 +585,9 @@ impl UseSpans<'_> {
 
     /// Add a span label to the arguments of the closure, if it exists.
     #[allow(rustc::diagnostic_outside_of_impl)]
-    pub(super) fn args_subdiag(
-        self,
-        dcx: &DiagCtxt,
-        err: &mut Diag<'_>,
-        f: impl FnOnce(Span) -> CaptureArgLabel,
-    ) {
+    pub(super) fn args_subdiag(self, err: &mut Diag<'_>, f: impl FnOnce(Span) -> CaptureArgLabel) {
         if let UseSpans::ClosureUse { args_span, .. } = self {
-            err.subdiagnostic(dcx, f(args_span));
+            err.subdiagnostic(f(args_span));
         }
     }
 
@@ -607,7 +596,6 @@ impl UseSpans<'_> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub(super) fn var_path_only_subdiag(
         self,
-        dcx: &DiagCtxt,
         err: &mut Diag<'_>,
         action: crate::InitializationRequiringAction,
     ) {
@@ -616,26 +604,20 @@ impl UseSpans<'_> {
         if let UseSpans::ClosureUse { closure_kind, path_span, .. } = self {
             match closure_kind {
                 hir::ClosureKind::Coroutine(_) => {
-                    err.subdiagnostic(
-                        dcx,
-                        match action {
-                            Borrow => BorrowInCoroutine { path_span },
-                            MatchOn | Use => UseInCoroutine { path_span },
-                            Assignment => AssignInCoroutine { path_span },
-                            PartialAssignment => AssignPartInCoroutine { path_span },
-                        },
-                    );
+                    err.subdiagnostic(match action {
+                        Borrow => BorrowInCoroutine { path_span },
+                        MatchOn | Use => UseInCoroutine { path_span },
+                        Assignment => AssignInCoroutine { path_span },
+                        PartialAssignment => AssignPartInCoroutine { path_span },
+                    });
                 }
                 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
-                    err.subdiagnostic(
-                        dcx,
-                        match action {
-                            Borrow => BorrowInClosure { path_span },
-                            MatchOn | Use => UseInClosure { path_span },
-                            Assignment => AssignInClosure { path_span },
-                            PartialAssignment => AssignPartInClosure { path_span },
-                        },
-                    );
+                    err.subdiagnostic(match action {
+                        Borrow => BorrowInClosure { path_span },
+                        MatchOn | Use => UseInClosure { path_span },
+                        Assignment => AssignInClosure { path_span },
+                        PartialAssignment => AssignPartInClosure { path_span },
+                    });
                 }
             }
         }
@@ -645,32 +627,28 @@ impl UseSpans<'_> {
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub(super) fn var_subdiag(
         self,
-        dcx: &DiagCtxt,
         err: &mut Diag<'_>,
         kind: Option<rustc_middle::mir::BorrowKind>,
         f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
     ) {
         if let UseSpans::ClosureUse { closure_kind, capture_kind_span, path_span, .. } = self {
             if capture_kind_span != path_span {
-                err.subdiagnostic(
-                    dcx,
-                    match kind {
-                        Some(kd) => match kd {
-                            rustc_middle::mir::BorrowKind::Shared
-                            | rustc_middle::mir::BorrowKind::Fake(_) => {
-                                CaptureVarKind::Immut { kind_span: capture_kind_span }
-                            }
+                err.subdiagnostic(match kind {
+                    Some(kd) => match kd {
+                        rustc_middle::mir::BorrowKind::Shared
+                        | rustc_middle::mir::BorrowKind::Fake(_) => {
+                            CaptureVarKind::Immut { kind_span: capture_kind_span }
+                        }
 
-                            rustc_middle::mir::BorrowKind::Mut { .. } => {
-                                CaptureVarKind::Mut { kind_span: capture_kind_span }
-                            }
-                        },
-                        None => CaptureVarKind::Move { kind_span: capture_kind_span },
+                        rustc_middle::mir::BorrowKind::Mut { .. } => {
+                            CaptureVarKind::Mut { kind_span: capture_kind_span }
+                        }
                     },
-                );
+                    None => CaptureVarKind::Move { kind_span: capture_kind_span },
+                });
             };
             let diag = f(closure_kind, path_span);
-            err.subdiagnostic(dcx, diag);
+            err.subdiagnostic(diag);
         }
     }
 
@@ -1042,15 +1020,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 CallKind::FnCall { fn_trait_id, self_ty }
                     if self.infcx.tcx.is_lang_item(fn_trait_id, LangItem::FnOnce) =>
                 {
-                    err.subdiagnostic(
-                        self.dcx(),
-                        CaptureReasonLabel::Call {
-                            fn_call_span,
-                            place_name: &place_name,
-                            is_partial,
-                            is_loop_message,
-                        },
-                    );
+                    err.subdiagnostic(CaptureReasonLabel::Call {
+                        fn_call_span,
+                        place_name: &place_name,
+                        is_partial,
+                        is_loop_message,
+                    });
                     // Check if the move occurs on a value because of a call on a closure that comes
                     // from a type parameter `F: FnOnce()`. If so, we provide a targeted `note`:
                     // ```
@@ -1119,27 +1094,20 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         );
                         err.span_note(span, fluent::borrowck_moved_a_fn_once_in_call_call);
                     } else {
-                        err.subdiagnostic(
-                            self.dcx(),
-                            CaptureReasonNote::FnOnceMoveInCall { var_span },
-                        );
+                        err.subdiagnostic(CaptureReasonNote::FnOnceMoveInCall { var_span });
                     }
                 }
                 CallKind::Operator { self_arg, trait_id, .. } => {
                     let self_arg = self_arg.unwrap();
-                    err.subdiagnostic(
-                        self.dcx(),
-                        CaptureReasonLabel::OperatorUse {
-                            fn_call_span,
-                            place_name: &place_name,
-                            is_partial,
-                            is_loop_message,
-                        },
-                    );
+                    err.subdiagnostic(CaptureReasonLabel::OperatorUse {
+                        fn_call_span,
+                        place_name: &place_name,
+                        is_partial,
+                        is_loop_message,
+                    });
                     if self.fn_self_span_reported.insert(fn_span) {
                         let lang = self.infcx.tcx.lang_items();
                         err.subdiagnostic(
-                            self.dcx(),
                             if [lang.not_trait(), lang.deref_trait(), lang.neg_trait()]
                                 .contains(&Some(trait_id))
                             {
@@ -1164,14 +1132,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         );
 
                         let func = tcx.def_path_str(method_did);
-                        err.subdiagnostic(
-                            self.dcx(),
-                            CaptureReasonNote::FuncTakeSelf {
-                                func,
-                                place_name: place_name.clone(),
-                                span: self_arg.span,
-                            },
-                        );
+                        err.subdiagnostic(CaptureReasonNote::FuncTakeSelf {
+                            func,
+                            place_name: place_name.clone(),
+                            span: self_arg.span,
+                        });
                     }
                     let parent_did = tcx.parent(method_did);
                     let parent_self_ty =
@@ -1185,10 +1150,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         matches!(tcx.get_diagnostic_name(def_id), Some(sym::Option | sym::Result))
                     });
                     if is_option_or_result && maybe_reinitialized_locations_is_empty {
-                        err.subdiagnostic(
-                            self.dcx(),
-                            CaptureReasonLabel::BorrowContent { var_span },
-                        );
+                        err.subdiagnostic(CaptureReasonLabel::BorrowContent { var_span });
                     }
                     if let Some((CallDesugaringKind::ForLoopIntoIter, _)) = desugaring {
                         let ty = moved_place.ty(self.body, tcx).ty;
@@ -1202,24 +1164,18 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             _ => false,
                         };
                         if suggest {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                CaptureReasonSuggest::IterateSlice {
-                                    ty,
-                                    span: move_span.shrink_to_lo(),
-                                },
-                            );
+                            err.subdiagnostic(CaptureReasonSuggest::IterateSlice {
+                                ty,
+                                span: move_span.shrink_to_lo(),
+                            });
                         }
 
-                        err.subdiagnostic(
-                            self.dcx(),
-                            CaptureReasonLabel::ImplicitCall {
-                                fn_call_span,
-                                place_name: &place_name,
-                                is_partial,
-                                is_loop_message,
-                            },
-                        );
+                        err.subdiagnostic(CaptureReasonLabel::ImplicitCall {
+                            fn_call_span,
+                            place_name: &place_name,
+                            is_partial,
+                            is_loop_message,
+                        });
                         // If the moved place was a `&mut` ref, then we can
                         // suggest to reborrow it where it was moved, so it
                         // will still be valid by the time we get to the usage.
@@ -1243,25 +1199,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                         }
                     } else {
                         if let Some((CallDesugaringKind::Await, _)) = desugaring {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                CaptureReasonLabel::Await {
-                                    fn_call_span,
-                                    place_name: &place_name,
-                                    is_partial,
-                                    is_loop_message,
-                                },
-                            );
+                            err.subdiagnostic(CaptureReasonLabel::Await {
+                                fn_call_span,
+                                place_name: &place_name,
+                                is_partial,
+                                is_loop_message,
+                            });
                         } else {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                CaptureReasonLabel::MethodCall {
-                                    fn_call_span,
-                                    place_name: &place_name,
-                                    is_partial,
-                                    is_loop_message,
-                                },
-                            );
+                            err.subdiagnostic(CaptureReasonLabel::MethodCall {
+                                fn_call_span,
+                                place_name: &place_name,
+                                is_partial,
+                                is_loop_message,
+                            });
                         }
                         // Erase and shadow everything that could be passed to the new infcx.
                         let ty = moved_place.ty(self.body, tcx).ty;
@@ -1276,12 +1226,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             )
                             && self.infcx.can_eq(self.param_env, ty, self_ty)
                         {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                CaptureReasonSuggest::FreshReborrow {
-                                    span: move_span.shrink_to_hi(),
-                                },
-                            );
+                            err.subdiagnostic(CaptureReasonSuggest::FreshReborrow {
+                                span: move_span.shrink_to_hi(),
+                            });
                             has_sugg = true;
                         }
                         if let Some(clone_trait) = tcx.lang_items().clone_trait() {
@@ -1368,20 +1315,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
             }
         } else {
             if move_span != span || is_loop_message {
-                err.subdiagnostic(
-                    self.dcx(),
-                    CaptureReasonLabel::MovedHere {
-                        move_span,
-                        is_partial,
-                        is_move_msg,
-                        is_loop_message,
-                    },
-                );
+                err.subdiagnostic(CaptureReasonLabel::MovedHere {
+                    move_span,
+                    is_partial,
+                    is_move_msg,
+                    is_loop_message,
+                });
             }
             // If the move error occurs due to a loop, don't show
             // another message for the same span
             if !is_loop_message {
-                move_spans.var_subdiag(self.dcx(), err, None, |kind, var_span| match kind {
+                move_spans.var_subdiag(err, None, |kind, var_span| match kind {
                     hir::ClosureKind::Coroutine(_) => {
                         CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
                     }
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index 1d844c3d6a2..5a7bca9ab03 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -579,15 +579,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         self.suggest_cloning(err, place_ty, expr, self.find_expr(other_span), None);
                     }
 
-                    err.subdiagnostic(
-                        self.dcx(),
-                        crate::session_diagnostics::TypeNoCopy::Label {
-                            is_partial_move: false,
-                            ty: place_ty,
-                            place: &place_desc,
-                            span,
-                        },
-                    );
+                    err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                        is_partial_move: false,
+                        ty: place_ty,
+                        place: &place_desc,
+                        span,
+                    });
                 } else {
                     binds_to.sort();
                     binds_to.dedup();
@@ -620,17 +617,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     );
                 }
 
-                err.subdiagnostic(
-                    self.dcx(),
-                    crate::session_diagnostics::TypeNoCopy::Label {
-                        is_partial_move: false,
-                        ty: place_ty,
-                        place: &place_desc,
-                        span: use_span,
-                    },
-                );
+                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                    is_partial_move: false,
+                    ty: place_ty,
+                    place: &place_desc,
+                    span: use_span,
+                });
 
-                use_spans.args_subdiag(self.dcx(), err, |args_span| {
+                use_spans.args_subdiag(err, |args_span| {
                     crate::session_diagnostics::CaptureArgLabel::MoveOutPlace {
                         place: place_desc,
                         args_span,
@@ -733,15 +727,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     self.suggest_cloning(err, bind_to.ty, expr, None, None);
                 }
 
-                err.subdiagnostic(
-                    self.dcx(),
-                    crate::session_diagnostics::TypeNoCopy::Label {
-                        is_partial_move: false,
-                        ty: bind_to.ty,
-                        place: place_desc,
-                        span: binding_span,
-                    },
-                );
+                err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {
+                    is_partial_move: false,
+                    ty: bind_to.ty,
+                    place: place_desc,
+                    span: binding_span,
+                });
             }
         }
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 9fd23bc94cf..e0b18536dd5 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -230,7 +230,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
                 if suggest {
                     borrow_spans.var_subdiag(
-                        self.dcx(),
                         &mut err,
                         Some(mir::BorrowKind::Mut { kind: mir::MutBorrowKind::Default }),
                         |_kind, var_span| {
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index 15a8764aab3..67c11ff4a5b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -631,13 +631,13 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 let upvars_map = self.infcx.tcx.upvars_mentioned(def_id).unwrap();
                 let upvar_def_span = self.infcx.tcx.hir().span(def_hir);
                 let upvar_span = upvars_map.get(&def_hir).unwrap().span;
-                diag.subdiagnostic(self.dcx(), VarHereDenote::Defined { span: upvar_def_span });
-                diag.subdiagnostic(self.dcx(), VarHereDenote::Captured { span: upvar_span });
+                diag.subdiagnostic(VarHereDenote::Defined { span: upvar_def_span });
+                diag.subdiagnostic(VarHereDenote::Captured { span: upvar_span });
             }
         }
 
         if let Some(fr_span) = self.give_region_a_name(*outlived_fr).unwrap().span() {
-            diag.subdiagnostic(self.dcx(), VarHereDenote::FnMutInferred { span: fr_span });
+            diag.subdiagnostic(VarHereDenote::FnMutInferred { span: fr_span });
         }
 
         self.suggest_move_on_borrowing_closure(&mut diag);
@@ -810,7 +810,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             },
         };
 
-        diag.subdiagnostic(self.dcx(), err_category);
+        diag.subdiagnostic(err_category);
 
         self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
         self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
@@ -1008,7 +1008,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     ident.span,
                     "calling this method introduces the `impl`'s `'static` requirement",
                 );
-                err.subdiagnostic(self.dcx(), RequireStaticErr::UsedImpl { multi_span });
+                err.subdiagnostic(RequireStaticErr::UsedImpl { multi_span });
                 err.span_suggestion_verbose(
                     span.shrink_to_hi(),
                     "consider relaxing the implicit `'static` requirement",
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 1a7961bf70c..64238e81b26 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -46,7 +46,7 @@ pub fn parse_asm_args<'a>(
     sp: Span,
     is_global_asm: bool,
 ) -> PResult<'a, AsmArgs> {
-    let dcx = &p.psess.dcx;
+    let dcx = p.dcx();
 
     if p.token == token::Eof {
         return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
@@ -307,7 +307,7 @@ pub fn parse_asm_args<'a>(
 fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
     // Tool-only output
     let full_span = if p.token.kind == token::Comma { span.to(p.token.span) } else { span };
-    p.psess.dcx.emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
+    p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
 }
 
 /// Try to set the provided option in the provided `AsmArgs`.
@@ -379,7 +379,7 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
     p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
 
     if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
-        return Err(p.psess.dcx.create_err(errors::NonABI { span: p.token.span }));
+        return Err(p.dcx().create_err(errors::NonABI { span: p.token.span }));
     }
 
     let mut new_abis = Vec::new();
@@ -390,7 +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.psess.dcx.struct_span_err(span, "expected string literal");
+                let mut err = p.dcx().struct_span_err(span, "expected string literal");
                 err.span_label(span, "not a string literal");
                 return Err(err);
             }
diff --git a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
index 16184ec7511..58928815e89 100644
--- a/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
+++ b/compiler/rustc_builtin_macros/src/cmdline_attrs.rs
@@ -26,7 +26,7 @@ pub fn inject(krate: &mut ast::Crate, psess: &ParseSess, attrs: &[String]) {
         };
         let end_span = parser.token.span;
         if parser.token != token::Eof {
-            psess.dcx.emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
+            psess.dcx().emit_err(errors::InvalidCrateAttr { span: start_span.to(end_span) });
             continue;
         }
 
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index b14eb2b5ee6..ed2f98f2a39 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -1,5 +1,5 @@
 use rustc_errors::{
-    codes::*, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan,
+    codes::*, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
     SingleLabelManySpans, SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -434,7 +434,7 @@ pub(crate) struct EnvNotDefinedWithUserMessage {
 // Hand-written implementation to support custom user messages.
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for EnvNotDefinedWithUserMessage {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         #[expect(
             rustc::untranslatable_diagnostic,
             reason = "cannot translate user-provided messages"
@@ -801,7 +801,7 @@ pub(crate) struct AsmClobberNoReg {
 }
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AsmClobberNoReg {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         // eager translation as `span_labels` takes `AsRef<str>`
         let lbl1 = dcx.eagerly_translate_to_string(
             crate::fluent_generated::builtin_macros_asm_clobber_abi,
diff --git a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
index 477e5c8bec5..99d0191958d 100644
--- a/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
+++ b/compiler/rustc_builtin_macros/src/proc_macro_harness.rs
@@ -3,6 +3,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{self as ast, attr, NodeId};
 use rustc_ast_pretty::pprust;
+use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
@@ -38,7 +39,7 @@ enum ProcMacro {
 struct CollectProcMacros<'a> {
     macros: Vec<ProcMacro>,
     in_root: bool,
-    dcx: &'a rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'a>,
     source_map: &'a SourceMap,
     is_proc_macro_crate: bool,
     is_test_crate: bool,
@@ -52,7 +53,7 @@ pub fn inject(
     is_proc_macro_crate: bool,
     has_proc_macro_decls: bool,
     is_test_crate: bool,
-    dcx: &rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) {
     let ecfg = ExpansionConfig::default("proc_macro".to_string(), features);
     let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index ba4e5cfd130..9d032eb190a 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -6,6 +6,7 @@ use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{walk_item, Visitor};
 use rustc_ast::{attr, ModKind};
+use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
@@ -391,7 +392,7 @@ fn get_test_name(i: &ast::Item) -> Option<Symbol> {
     attr::first_attr_value_str_by_name(&i.attrs, sym::rustc_test_marker)
 }
 
-fn get_test_runner(dcx: &rustc_errors::DiagCtxt, krate: &ast::Crate) -> Option<ast::Path> {
+fn get_test_runner(dcx: DiagCtxtHandle<'_>, krate: &ast::Crate) -> Option<ast::Path> {
     let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
     let meta_list = test_attr.meta_item_list()?;
     let span = test_attr.span;
diff --git a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
index a73860cf18b..2093b49ff31 100644
--- a/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
+++ b/compiler/rustc_codegen_cranelift/src/concurrency_limiter.rs
@@ -1,6 +1,7 @@
 use std::sync::{Arc, Condvar, Mutex};
 
 use jobserver::HelperThread;
+use rustc_errors::DiagCtxtHandle;
 use rustc_session::Session;
 
 // FIXME don't panic when a worker thread panics
@@ -46,7 +47,7 @@ impl ConcurrencyLimiter {
         }
     }
 
-    pub(super) fn acquire(&self, dcx: &rustc_errors::DiagCtxt) -> ConcurrencyLimiterToken {
+    pub(super) fn acquire(&self, dcx: DiagCtxtHandle<'_>) -> ConcurrencyLimiterToken {
         let mut state = self.state.lock().unwrap();
         loop {
             state.assert_invariants();
diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs
index 61e0f203ee0..ec70fbdddb0 100644
--- a/compiler/rustc_codegen_gcc/src/back/lto.rs
+++ b/compiler/rustc_codegen_gcc/src/back/lto.rs
@@ -28,7 +28,7 @@ use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput};
 use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
 use rustc_data_structures::memmap::Mmap;
-use rustc_errors::{DiagCtxt, FatalError};
+use rustc_errors::{DiagCtxtHandle, FatalError};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::dep_graph::WorkProduct;
 use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
@@ -59,7 +59,7 @@ struct LtoData {
 
 fn prepare_lto(
     cgcx: &CodegenContext<GccCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> Result<LtoData, FatalError> {
     let export_threshold = match cgcx.lto {
         // We're just doing LTO for our one crate
@@ -179,12 +179,13 @@ pub(crate) fn run_fat(
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<LtoModuleCodegen<GccCodegenBackend>, FatalError> {
     let dcx = cgcx.create_dcx();
-    let lto_data = prepare_lto(cgcx, &dcx)?;
+    let dcx = dcx.handle();
+    let lto_data = prepare_lto(cgcx, dcx)?;
     /*let symbols_below_threshold =
     lto_data.symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();*/
     fat_lto(
         cgcx,
-        &dcx,
+        dcx,
         modules,
         cached_modules,
         lto_data.upstream_modules,
@@ -195,7 +196,7 @@ pub(crate) fn run_fat(
 
 fn fat_lto(
     cgcx: &CodegenContext<GccCodegenBackend>,
-    _dcx: &DiagCtxt,
+    _dcx: DiagCtxtHandle<'_>,
     modules: Vec<FatLtoInput<GccCodegenBackend>>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
     mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
diff --git a/compiler/rustc_codegen_gcc/src/back/write.rs b/compiler/rustc_codegen_gcc/src/back/write.rs
index 3ea5be1ee56..b9c7f72d0b7 100644
--- a/compiler/rustc_codegen_gcc/src/back/write.rs
+++ b/compiler/rustc_codegen_gcc/src/back/write.rs
@@ -4,7 +4,7 @@ use gccjit::OutputKind;
 use rustc_codegen_ssa::back::link::ensure_removed;
 use rustc_codegen_ssa::back::write::{BitcodeSection, CodegenContext, EmitObj, ModuleConfig};
 use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
-use rustc_errors::DiagCtxt;
+use rustc_errors::DiagCtxtHandle;
 use rustc_fs_util::link_or_copy;
 use rustc_session::config::OutputType;
 use rustc_span::fatal_error::FatalError;
@@ -15,7 +15,7 @@ use crate::{GccCodegenBackend, GccContext};
 
 pub(crate) unsafe fn codegen(
     cgcx: &CodegenContext<GccCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     module: ModuleCodegen<GccContext>,
     config: &ModuleConfig,
 ) -> Result<CompiledModule, FatalError> {
@@ -166,7 +166,7 @@ pub(crate) unsafe fn codegen(
 
 pub(crate) fn link(
     _cgcx: &CodegenContext<GccCodegenBackend>,
-    _dcx: &DiagCtxt,
+    _dcx: DiagCtxtHandle<'_>,
     mut _modules: Vec<ModuleCodegen<GccContext>>,
 ) -> Result<ModuleCodegen<GccContext>, FatalError> {
     unimplemented!();
diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs
index aee2b077dba..6bada3d334c 100644
--- a/compiler/rustc_codegen_gcc/src/errors.rs
+++ b/compiler/rustc_codegen_gcc/src/errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
 
@@ -90,13 +90,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 pub(crate) struct MissingFeatures;
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::codegen_gcc_target_feature_disable_or_enable);
         if let Some(span) = self.span {
             diag.span(span);
         };
         if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(dcx, missing_features);
+            diag.subdiagnostic(missing_features);
         }
         diag.arg("features", self.features.join(", "));
         diag
diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs
index b1785e150ad..24856506c46 100644
--- a/compiler/rustc_codegen_gcc/src/lib.rs
+++ b/compiler/rustc_codegen_gcc/src/lib.rs
@@ -16,13 +16,7 @@
 #![allow(internal_features)]
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
-#![feature(
-    rustc_private,
-    decl_macro,
-    never_type,
-    trusted_len,
-    hash_raw_entry
-)]
+#![feature(rustc_private, decl_macro, never_type, trusted_len, hash_raw_entry)]
 #![allow(broken_intra_doc_links)]
 #![recursion_limit = "256"]
 #![warn(rust_2018_idioms)]
@@ -104,7 +98,7 @@ use rustc_codegen_ssa::traits::{
 use rustc_codegen_ssa::{CodegenResults, CompiledModule, ModuleCodegen};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync::IntoDynSyncSend;
-use rustc_errors::{DiagCtxt, ErrorGuaranteed};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::ty::TyCtxt;
@@ -386,7 +380,7 @@ impl WriteBackendMethods for GccCodegenBackend {
 
     unsafe fn optimize(
         _cgcx: &CodegenContext<Self>,
-        _dcx: &DiagCtxt,
+        _dcx: DiagCtxtHandle<'_>,
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError> {
@@ -411,14 +405,17 @@ impl WriteBackendMethods for GccCodegenBackend {
 
     unsafe fn codegen(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         module: ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<CompiledModule, FatalError> {
         back::write::codegen(cgcx, dcx, module, config)
     }
 
-    fn prepare_thin(_module: ModuleCodegen<Self::Module>, _emit_summary: bool) -> (String, Self::ThinBuffer) {
+    fn prepare_thin(
+        _module: ModuleCodegen<Self::Module>,
+        _emit_summary: bool,
+    ) -> (String, Self::ThinBuffer) {
         unimplemented!();
     }
 
@@ -428,7 +425,7 @@ impl WriteBackendMethods for GccCodegenBackend {
 
     fn run_link(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         modules: Vec<ModuleCodegen<Self::Module>>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
         back::write::link(cgcx, dcx, modules)
diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs
index bb7c8fe2ea7..aff3e3d7076 100644
--- a/compiler/rustc_codegen_llvm/src/back/lto.rs
+++ b/compiler/rustc_codegen_llvm/src/back/lto.rs
@@ -14,7 +14,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::memmap::Mmap;
-use rustc_errors::{DiagCtxt, FatalError};
+use rustc_errors::{DiagCtxtHandle, FatalError};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::bug;
 use rustc_middle::dep_graph::WorkProduct;
@@ -49,7 +49,7 @@ pub fn crate_type_allows_lto(crate_type: CrateType) -> bool {
 
 fn prepare_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> Result<(Vec<CString>, Vec<(SerializedModule<ModuleBuffer>, CString)>), FatalError> {
     let export_threshold = match cgcx.lto {
         // We're just doing LTO for our one crate
@@ -203,10 +203,11 @@ pub(crate) fn run_fat(
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<LtoModuleCodegen<LlvmCodegenBackend>, FatalError> {
     let dcx = cgcx.create_dcx();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?;
+    let dcx = dcx.handle();
+    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
-    fat_lto(cgcx, &dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
+    fat_lto(cgcx, dcx, modules, cached_modules, upstream_modules, &symbols_below_threshold)
 }
 
 /// Performs thin LTO by performing necessary global analysis and returning two
@@ -218,7 +219,8 @@ pub(crate) fn run_thin(
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
 ) -> Result<(Vec<LtoModuleCodegen<LlvmCodegenBackend>>, Vec<WorkProduct>), FatalError> {
     let dcx = cgcx.create_dcx();
-    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, &dcx)?;
+    let dcx = dcx.handle();
+    let (symbols_below_threshold, upstream_modules) = prepare_lto(cgcx, dcx)?;
     let symbols_below_threshold =
         symbols_below_threshold.iter().map(|c| c.as_ptr()).collect::<Vec<_>>();
     if cgcx.opts.cg.linker_plugin_lto.enabled() {
@@ -227,7 +229,7 @@ pub(crate) fn run_thin(
                       is deferred to the linker"
         );
     }
-    thin_lto(cgcx, &dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
+    thin_lto(cgcx, dcx, modules, upstream_modules, cached_modules, &symbols_below_threshold)
 }
 
 pub(crate) fn prepare_thin(
@@ -241,7 +243,7 @@ pub(crate) fn prepare_thin(
 
 fn fat_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     modules: Vec<FatLtoInput<LlvmCodegenBackend>>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
     mut serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
@@ -436,7 +438,7 @@ impl Drop for Linker<'_> {
 /// they all go out of scope.
 fn thin_lto(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     modules: Vec<(String, ThinBuffer)>,
     serialized_modules: Vec<(SerializedModule<ModuleBuffer>, CString)>,
     cached_modules: Vec<(SerializedModule<ModuleBuffer>, WorkProduct)>,
@@ -593,7 +595,7 @@ fn thin_lto(
 
 pub(crate) fn run_pass_manager(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     module: &mut ModuleCodegen<ModuleLlvm>,
     thin: bool,
 ) -> Result<(), FatalError> {
@@ -714,10 +716,11 @@ pub unsafe fn optimize_thin_module(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
     let dcx = cgcx.create_dcx();
+    let dcx = dcx.handle();
 
     let module_name = &thin_module.shared.module_names[thin_module.idx];
     let tm_factory_config = TargetMachineFactoryConfig::new(cgcx, module_name.to_str().unwrap());
-    let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(&dcx, e))?;
+    let tm = (cgcx.tm_factory)(tm_factory_config).map_err(|e| write::llvm_err(dcx, e))?;
 
     // Right now the implementation we've got only works over serialized
     // modules, so we create a fresh new LLVM context and parse the module
@@ -725,7 +728,7 @@ pub unsafe fn optimize_thin_module(
     // crates but for locally codegened modules we may be able to reuse
     // that LLVM Context and Module.
     let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
-    let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &dcx)? as *const _;
+    let llmod_raw = parse_module(llcx, module_name, thin_module.data(), dcx)? as *const _;
     let mut module = ModuleCodegen {
         module_llvm: ModuleLlvm { llmod_raw, llcx, tm: ManuallyDrop::new(tm) },
         name: thin_module.name().to_string(),
@@ -748,7 +751,7 @@ pub unsafe fn optimize_thin_module(
             let _timer =
                 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_rename", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTORename(thin_module.shared.data.0, llmod, target) {
-                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-rename");
         }
@@ -758,7 +761,7 @@ pub unsafe fn optimize_thin_module(
                 .prof
                 .generic_activity_with_arg("LLVM_thin_lto_resolve_weak", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOResolveWeak(thin_module.shared.data.0, llmod) {
-                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-resolve");
         }
@@ -768,7 +771,7 @@ pub unsafe fn optimize_thin_module(
                 .prof
                 .generic_activity_with_arg("LLVM_thin_lto_internalize", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOInternalize(thin_module.shared.data.0, llmod) {
-                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-internalize");
         }
@@ -777,7 +780,7 @@ pub unsafe fn optimize_thin_module(
             let _timer =
                 cgcx.prof.generic_activity_with_arg("LLVM_thin_lto_import", thin_module.name());
             if !llvm::LLVMRustPrepareThinLTOImport(thin_module.shared.data.0, llmod, target) {
-                return Err(write::llvm_err(&dcx, LlvmError::PrepareThinLtoModule));
+                return Err(write::llvm_err(dcx, LlvmError::PrepareThinLtoModule));
             }
             save_temp_bitcode(cgcx, &module, "thin-lto-after-import");
         }
@@ -789,7 +792,7 @@ pub unsafe fn optimize_thin_module(
         // little differently.
         {
             info!("running thin lto passes over {}", module.name);
-            run_pass_manager(cgcx, &dcx, &mut module, true)?;
+            run_pass_manager(cgcx, dcx, &mut module, true)?;
             save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
         }
     }
@@ -859,7 +862,7 @@ pub fn parse_module<'a>(
     cx: &'a llvm::Context,
     name: &CStr,
     data: &[u8],
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> Result<&'a llvm::Module, FatalError> {
     unsafe {
         llvm::LLVMRustParseBitcodeForLTO(cx, data.as_ptr(), data.len(), name.as_ptr())
diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs
index 02e3bb06dda..bbfc697407b 100644
--- a/compiler/rustc_codegen_llvm/src/back/write.rs
+++ b/compiler/rustc_codegen_llvm/src/back/write.rs
@@ -26,7 +26,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::{CompiledModule, ModuleCodegen};
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_errors::{DiagCtxt, FatalError, Level};
+use rustc_errors::{DiagCtxtHandle, FatalError, Level};
 use rustc_fs_util::{link_or_copy, path_to_c_string};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{self, Lto, OutputType, Passes};
@@ -47,7 +47,7 @@ use std::slice;
 use std::str;
 use std::sync::Arc;
 
-pub fn llvm_err<'a>(dcx: &rustc_errors::DiagCtxt, err: LlvmError<'a>) -> FatalError {
+pub fn llvm_err<'a>(dcx: DiagCtxtHandle<'_>, err: LlvmError<'a>) -> FatalError {
     match llvm::last_error() {
         Some(llvm_err) => dcx.emit_almost_fatal(WithLlvmError(err, llvm_err)),
         None => dcx.emit_almost_fatal(err),
@@ -55,7 +55,7 @@ pub fn llvm_err<'a>(dcx: &rustc_errors::DiagCtxt, err: LlvmError<'a>) -> FatalEr
 }
 
 pub fn write_output_file<'ll>(
-    dcx: &rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     target: &'ll llvm::TargetMachine,
     pm: &llvm::PassManager<'ll>,
     m: &'ll llvm::Module,
@@ -331,7 +331,7 @@ pub enum CodegenDiagnosticsStage {
 }
 
 pub struct DiagnosticHandlers<'a> {
-    data: *mut (&'a CodegenContext<LlvmCodegenBackend>, &'a DiagCtxt),
+    data: *mut (&'a CodegenContext<LlvmCodegenBackend>, DiagCtxtHandle<'a>),
     llcx: &'a llvm::Context,
     old_handler: Option<&'a llvm::DiagnosticHandler>,
 }
@@ -339,7 +339,7 @@ pub struct DiagnosticHandlers<'a> {
 impl<'a> DiagnosticHandlers<'a> {
     pub fn new(
         cgcx: &'a CodegenContext<LlvmCodegenBackend>,
-        dcx: &'a DiagCtxt,
+        dcx: DiagCtxtHandle<'a>,
         llcx: &'a llvm::Context,
         module: &ModuleCodegen<ModuleLlvm>,
         stage: CodegenDiagnosticsStage,
@@ -428,7 +428,7 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
     if user.is_null() {
         return;
     }
-    let (cgcx, dcx) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, &DiagCtxt));
+    let (cgcx, dcx) = *(user as *const (&CodegenContext<LlvmCodegenBackend>, DiagCtxtHandle<'_>));
 
     match llvm::diagnostic::Diagnostic::unpack(info) {
         llvm::diagnostic::InlineAsm(inline) => {
@@ -506,7 +506,7 @@ fn get_instr_profile_output_path(config: &ModuleConfig) -> Option<CString> {
 
 pub(crate) unsafe fn llvm_optimize(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
     opt_level: config::OptLevel,
@@ -604,7 +604,7 @@ pub(crate) unsafe fn llvm_optimize(
 // Unsafe due to LLVM calls.
 pub(crate) unsafe fn optimize(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     module: &ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<(), FatalError> {
@@ -637,7 +637,7 @@ pub(crate) unsafe fn optimize(
 
 pub(crate) fn link(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     mut modules: Vec<ModuleCodegen<ModuleLlvm>>,
 ) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
     use super::lto::{Linker, ModuleBuffer};
@@ -660,7 +660,7 @@ pub(crate) fn link(
 
 pub(crate) unsafe fn codegen(
     cgcx: &CodegenContext<LlvmCodegenBackend>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     module: ModuleCodegen<ModuleLlvm>,
     config: &ModuleConfig,
 ) -> Result<CompiledModule, FatalError> {
diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs
index 9d83dc81163..40ac2f9c8ba 100644
--- a/compiler/rustc_codegen_llvm/src/errors.rs
+++ b/compiler/rustc_codegen_llvm/src/errors.rs
@@ -4,7 +4,7 @@ use std::path::Path;
 
 use crate::fluent_generated as fluent;
 use rustc_data_structures::small_c_str::SmallCStr;
-use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
 
@@ -100,7 +100,7 @@ pub(crate) struct DynamicLinkingWithLTO;
 pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>);
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for ParseTargetMachineConfig<'_> {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let diag: Diag<'_, G> = self.0.into_diag(dcx, level);
         let (message, _) = diag.messages.first().expect("`LlvmError` with no message");
         let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter());
@@ -120,13 +120,13 @@ pub(crate) struct TargetFeatureDisableOrEnable<'a> {
 pub(crate) struct MissingFeatures;
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetFeatureDisableOrEnable<'_> {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::codegen_llvm_target_feature_disable_or_enable);
         if let Some(span) = self.span {
             diag.span(span);
         };
         if let Some(missing_features) = self.missing_features {
-            diag.subdiagnostic(dcx, missing_features);
+            diag.subdiagnostic(missing_features);
         }
         diag.arg("features", self.features.join(", "));
         diag
@@ -180,7 +180,7 @@ pub enum LlvmError<'a> {
 pub(crate) struct WithLlvmError<'a>(pub LlvmError<'a>, pub String);
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for WithLlvmError<'_> {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         use LlvmError::*;
         let msg_with_llvm_err = match &self.0 {
             WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err,
diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs
index bb76d388393..4b7a2643007 100644
--- a/compiler/rustc_codegen_llvm/src/lib.rs
+++ b/compiler/rustc_codegen_llvm/src/lib.rs
@@ -31,7 +31,7 @@ use rustc_codegen_ssa::traits::*;
 use rustc_codegen_ssa::ModuleCodegen;
 use rustc_codegen_ssa::{CodegenResults, CompiledModule};
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
 use rustc_metadata::EncodedMetadata;
 use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
 use rustc_middle::ty::TyCtxt;
@@ -191,7 +191,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     fn run_link(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         modules: Vec<ModuleCodegen<Self::Module>>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError> {
         back::write::link(cgcx, dcx, modules)
@@ -212,7 +212,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     unsafe fn optimize(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError> {
@@ -223,7 +223,8 @@ impl WriteBackendMethods for LlvmCodegenBackend {
         module: &mut ModuleCodegen<Self::Module>,
     ) -> Result<(), FatalError> {
         let dcx = cgcx.create_dcx();
-        back::lto::run_pass_manager(cgcx, &dcx, module, false)
+        let dcx = dcx.handle();
+        back::lto::run_pass_manager(cgcx, dcx, module, false)
     }
     unsafe fn optimize_thin(
         cgcx: &CodegenContext<Self>,
@@ -233,7 +234,7 @@ impl WriteBackendMethods for LlvmCodegenBackend {
     }
     unsafe fn codegen(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         module: ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<CompiledModule, FatalError> {
@@ -441,7 +442,7 @@ impl ModuleLlvm {
         cgcx: &CodegenContext<LlvmCodegenBackend>,
         name: &CStr,
         buffer: &[u8],
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
     ) -> Result<Self, FatalError> {
         unsafe {
             let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index bdb808b1d4f..c352100b01b 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::temp_dir::MaybeTempDir;
-use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
 use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize};
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
 use rustc_metadata::find_native_static_library;
@@ -54,7 +54,7 @@ use std::process::{ExitStatus, Output, Stdio};
 use std::{env, fmt, fs, io, mem, str};
 use tracing::{debug, info, warn};
 
-pub fn ensure_removed(dcx: &DiagCtxt, path: &Path) {
+pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) {
     if let Err(e) = fs::remove_file(path) {
         if e.kind() != io::ErrorKind::NotFound {
             dcx.err(format!("failed to remove {}: {}", path.display(), e));
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index dec87db0fc5..064be4988bd 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -891,9 +891,10 @@ fn execute_optimize_work_item<B: ExtraBackendMethods>(
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
     let dcx = cgcx.create_dcx();
+    let dcx = dcx.handle();
 
     unsafe {
-        B::optimize(cgcx, &dcx, &module, module_config)?;
+        B::optimize(cgcx, dcx, &module, module_config)?;
     }
 
     // After we've done the initial round of optimizations we need to
@@ -954,7 +955,11 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
         match link_or_copy(&source_file, &output_path) {
             Ok(_) => Some(output_path),
             Err(error) => {
-                cgcx.create_dcx().emit_err(errors::CopyPathBuf { source_file, output_path, error });
+                cgcx.create_dcx().handle().emit_err(errors::CopyPathBuf {
+                    source_file,
+                    output_path,
+                    error,
+                });
                 None
             }
         }
@@ -987,7 +992,7 @@ fn execute_copy_from_cache_work_item<B: ExtraBackendMethods>(
     let bytecode = load_from_incr_cache(module_config.emit_bc, OutputType::Bitcode);
     let object = load_from_incr_cache(should_emit_obj, OutputType::Object);
     if should_emit_obj && object.is_none() {
-        cgcx.create_dcx().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
+        cgcx.create_dcx().handle().emit_fatal(errors::NoSavedObjectFile { cgu_name: &module.name })
     }
 
     WorkItemResult::Finished(CompiledModule {
@@ -1016,12 +1021,13 @@ fn finish_intra_module_work<B: ExtraBackendMethods>(
     module_config: &ModuleConfig,
 ) -> Result<WorkItemResult<B>, FatalError> {
     let dcx = cgcx.create_dcx();
+    let dcx = dcx.handle();
 
     if !cgcx.opts.unstable_opts.combine_cgu
         || module.kind == ModuleKind::Metadata
         || module.kind == ModuleKind::Allocator
     {
-        let module = unsafe { B::codegen(cgcx, &dcx, module, module_config)? };
+        let module = unsafe { B::codegen(cgcx, dcx, module, module_config)? };
         Ok(WorkItemResult::Finished(module))
     } else {
         Ok(WorkItemResult::NeedsLink(module))
@@ -1692,9 +1698,10 @@ fn start_executing_work<B: ExtraBackendMethods>(
         if !needs_link.is_empty() {
             assert!(compiled_modules.is_empty());
             let dcx = cgcx.create_dcx();
-            let module = B::run_link(&cgcx, &dcx, needs_link).map_err(|_| ())?;
+            let dcx = dcx.handle();
+            let module = B::run_link(&cgcx, dcx, needs_link).map_err(|_| ())?;
             let module = unsafe {
-                B::codegen(&cgcx, &dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?
+                B::codegen(&cgcx, dcx, module, cgcx.config(ModuleKind::Regular)).map_err(|_| ())?
             };
             compiled_modules.push(module);
         }
diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs
index 3641e7842cf..e6ba31c5165 100644
--- a/compiler/rustc_codegen_ssa/src/errors.rs
+++ b/compiler/rustc_codegen_ssa/src/errors.rs
@@ -4,7 +4,7 @@ use crate::assert_module_sources::CguReuse;
 use crate::back::command::Command;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
+    codes::*, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::layout::LayoutError;
@@ -215,7 +215,7 @@ pub enum LinkRlibError {
 pub struct ThorinErrorWrapper(pub thorin::Error);
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper {
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let build = |msg| Diag::new(dcx, level, msg);
         match self.0 {
             thorin::Error::ReadInput(_) => build(fluent::codegen_ssa_thorin_read_input_failure),
@@ -348,7 +348,7 @@ pub struct LinkingFailed<'a> {
 }
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed);
         diag.arg("linker_path", format!("{}", self.linker_path.display()));
         diag.arg("exit_status", format!("{}", self.exit_status));
diff --git a/compiler/rustc_codegen_ssa/src/traits/write.rs b/compiler/rustc_codegen_ssa/src/traits/write.rs
index f83e34ab01b..f4b1421a532 100644
--- a/compiler/rustc_codegen_ssa/src/traits/write.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/write.rs
@@ -2,7 +2,7 @@ use crate::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule};
 use crate::back::write::{CodegenContext, FatLtoInput, ModuleConfig};
 use crate::{CompiledModule, ModuleCodegen};
 
-use rustc_errors::{DiagCtxt, FatalError};
+use rustc_errors::{DiagCtxtHandle, FatalError};
 use rustc_middle::dep_graph::WorkProduct;
 
 pub trait WriteBackendMethods: 'static + Sized + Clone {
@@ -16,7 +16,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     /// Merge all modules into main_module and returning it
     fn run_link(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         modules: Vec<ModuleCodegen<Self::Module>>,
     ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     /// Performs fat LTO by merging all modules into a single one and returning it
@@ -38,7 +38,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     fn print_statistics(&self);
     unsafe fn optimize(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         module: &ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<(), FatalError>;
@@ -52,7 +52,7 @@ pub trait WriteBackendMethods: 'static + Sized + Clone {
     ) -> Result<ModuleCodegen<Self::Module>, FatalError>;
     unsafe fn codegen(
         cgcx: &CodegenContext<Self>,
-        dcx: &DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         module: ModuleCodegen<Self::Module>,
         config: &ModuleConfig,
     ) -> Result<CompiledModule, FatalError>;
diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs
index 308b90cd470..ac8f0d842ee 100644
--- a/compiler/rustc_const_eval/src/check_consts/mod.rs
+++ b/compiler/rustc_const_eval/src/check_consts/mod.rs
@@ -5,7 +5,7 @@
 //! it finds operations that are invalid in a certain context.
 
 use rustc_attr as attr;
-use rustc_errors::DiagCtxt;
+use rustc_errors::DiagCtxtHandle;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::bug;
@@ -46,7 +46,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> {
         ConstCx { body, tcx, param_env, const_kind }
     }
 
-    pub(crate) fn dcx(&self) -> &'tcx DiagCtxt {
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {
         self.tcx.dcx()
     }
 
diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs
index eb9a83fb9cf..55432e63ef9 100644
--- a/compiler/rustc_const_eval/src/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/check_consts/ops.rs
@@ -138,7 +138,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                         // FIXME(effects) revisit this
                         if !tcx.is_const_trait_impl_raw(data.impl_def_id) {
                             let span = tcx.def_span(data.impl_def_id);
-                            err.subdiagnostic(tcx.dcx(), errors::NonConstImplNote { span });
+                            err.subdiagnostic(errors::NonConstImplNote { span });
                         }
                     }
                 }
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 91d17fdd895..5fa48a59794 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -2,7 +2,7 @@ use std::borrow::Cow;
 
 use either::Either;
 use rustc_errors::{
-    codes::*, Diag, DiagArgValue, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, Level,
+    codes::*, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, Level,
 };
 use rustc_hir::ConstContext;
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -453,7 +453,7 @@ pub trait ReportErrorExt {
     }
 }
 
-fn bad_pointer_message(msg: CheckInAllocMsg, dcx: &DiagCtxt) -> String {
+fn bad_pointer_message(msg: CheckInAllocMsg, dcx: DiagCtxtHandle<'_>) -> String {
     use crate::fluent_generated::*;
 
     let msg = match msg {
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 86325c5ae24..f602d989e08 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -4,7 +4,7 @@ use std::{fmt, mem};
 use either::{Either, Left, Right};
 use tracing::{debug, info, info_span, instrument, trace};
 
-use rustc_errors::DiagCtxt;
+use rustc_errors::DiagCtxtHandle;
 use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
 use rustc_index::IndexVec;
 use rustc_middle::mir;
@@ -474,7 +474,7 @@ pub(super) fn from_known_layout<'tcx>(
 ///
 /// This is NOT the preferred way to render an error; use `report` from `const_eval` instead.
 /// However, this is useful when error messages appear in ICEs.
-pub fn format_interp_error<'tcx>(dcx: &DiagCtxt, e: InterpErrorInfo<'tcx>) -> String {
+pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tcx>) -> String {
     let (e, backtrace) = e.into_parts();
     backtrace.print_backtrace();
     // FIXME(fee1-dead), HACK: we want to use the error as title therefore we can just extract the
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 9acff4a0a26..5ffa3a6099c 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -1444,6 +1444,7 @@ fn report_ice(
         fallback_bundle,
     ));
     let dcx = rustc_errors::DiagCtxt::new(emitter);
+    let dcx = dcx.handle();
 
     // a .span_bug or .bug call has already printed what
     // it wants to print.
@@ -1509,7 +1510,7 @@ fn report_ice(
 
     let num_frames = if backtrace { None } else { Some(2) };
 
-    interface::try_print_query_stack(&dcx, num_frames, file);
+    interface::try_print_query_stack(dcx, num_frames, file);
 
     // We don't trust this callback not to panic itself, so run it at the end after we're sure we've
     // printed all the relevant info.
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 34b569c4206..e580910af77 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -1,7 +1,7 @@
 use crate::snippet::Style;
 use crate::{
-    CodeSuggestion, DiagCtxt, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, MultiSpan,
-    StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
+    CodeSuggestion, DiagCtxtHandle, DiagMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level,
+    MultiSpan, StashKey, SubdiagMessage, Substitution, SubstitutionPart, SuggestionStyle,
 };
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
@@ -133,7 +133,7 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
 pub trait Diagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
     /// Write out as a diagnostic out of `DiagCtxt`.
     #[must_use]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G>;
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G>;
 }
 
 impl<'a, T, G> Diagnostic<'a, G> for Spanned<T>
@@ -141,7 +141,7 @@ where
     T: Diagnostic<'a, G>,
     G: EmissionGuarantee,
 {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         self.node.into_diag(dcx, level).with_span(self.span)
     }
 }
@@ -490,7 +490,7 @@ pub struct Subdiag {
 /// the methods of `Diag` here, consider extending `DiagCtxtFlags`.
 #[must_use]
 pub struct Diag<'a, G: EmissionGuarantee = ErrorGuaranteed> {
-    pub dcx: &'a DiagCtxt,
+    pub dcx: DiagCtxtHandle<'a>,
 
     /// Why the `Option`? It is always `Some` until the `Diag` is consumed via
     /// `emit`, `cancel`, etc. At that point it is consumed and replaced with
@@ -578,13 +578,13 @@ macro_rules! with_fn {
 impl<'a, G: EmissionGuarantee> Diag<'a, G> {
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn new(dcx: &'a DiagCtxt, level: Level, message: impl Into<DiagMessage>) -> Self {
+    pub fn new(dcx: DiagCtxtHandle<'a>, level: Level, message: impl Into<DiagMessage>) -> Self {
         Self::new_diagnostic(dcx, DiagInner::new(level, message))
     }
 
     /// Creates a new `Diag` with an already constructed diagnostic.
     #[track_caller]
-    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: DiagInner) -> Self {
+    pub(crate) fn new_diagnostic(dcx: DiagCtxtHandle<'a>, diag: DiagInner) -> Self {
         debug!("Created new diagnostic");
         Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
     }
@@ -1192,11 +1192,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
     /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
     /// interpolated variables).
     #[rustc_lint_diagnostics]
-    pub fn subdiagnostic(
-        &mut self,
-        dcx: &crate::DiagCtxt,
-        subdiagnostic: impl Subdiagnostic,
-    ) -> &mut Self {
+    pub fn subdiagnostic(&mut self, subdiagnostic: impl Subdiagnostic) -> &mut Self {
+        let dcx = self.dcx;
         subdiagnostic.add_to_diag_with(self, &|diag, msg| {
             let args = diag.args.iter();
             let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
@@ -1341,7 +1338,8 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
 
     /// See `DiagCtxt::stash_diagnostic` for details.
     pub fn stash(mut self, span: Span, key: StashKey) -> Option<ErrorGuaranteed> {
-        self.dcx.stash_diagnostic(span, key, self.take_diag())
+        let diag = self.take_diag();
+        self.dcx.stash_diagnostic(span, key, diag)
     }
 
     /// Delay emission of this diagnostic as a bug.
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index ee6df8e15db..0af80bc5c67 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,7 +1,7 @@
 use crate::diagnostic::DiagLocation;
-use crate::{fluent_generated as fluent, Subdiagnostic};
+use crate::{fluent_generated as fluent, DiagCtxtHandle, Subdiagnostic};
 use crate::{
-    Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level,
+    Diag, DiagArgValue, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level,
     SubdiagMessageOp,
 };
 use rustc_ast as ast;
@@ -315,7 +315,7 @@ impl IntoDiagArg for DiagSymbolList {
 }
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutErrors<'_> {
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         match self {
             TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
                 Diag::new(dcx, level, fluent::errors_target_invalid_address_space)
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index 5d4d2555100..245deda50d5 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -567,7 +567,7 @@ impl Emitter for SilentEmitter {
             if let Some(fatal_note) = &self.fatal_note {
                 diag.sub(Level::Note, fatal_note.clone(), MultiSpan::new());
             }
-            self.fatal_dcx.emit_diagnostic(diag);
+            self.fatal_dcx.handle().emit_diagnostic(diag);
         }
     }
 }
diff --git a/compiler/rustc_errors/src/json/tests.rs b/compiler/rustc_errors/src/json/tests.rs
index 80b4d2bf75c..e4b29fc9103 100644
--- a/compiler/rustc_errors/src/json/tests.rs
+++ b/compiler/rustc_errors/src/json/tests.rs
@@ -55,8 +55,7 @@ fn test_positions(code: &str, span: (u32, u32), expected_output: SpanTestData) {
         );
 
         let span = Span::with_root_ctxt(BytePos(span.0), BytePos(span.1));
-        let dcx = DiagCtxt::new(Box::new(je));
-        dcx.span_err(span, "foo");
+        DiagCtxt::new(Box::new(je)).handle().span_err(span, "foo");
 
         let bytes = output.lock().unwrap();
         let actual_output = str::from_utf8(&bytes).unwrap();
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 4dc5d84b318..83d5bbff0b0 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -414,6 +414,19 @@ pub struct DiagCtxt {
     inner: Lock<DiagCtxtInner>,
 }
 
+#[derive(Copy, Clone)]
+pub struct DiagCtxtHandle<'a> {
+    dcx: &'a DiagCtxt,
+}
+
+impl<'a> std::ops::Deref for DiagCtxtHandle<'a> {
+    type Target = &'a DiagCtxt;
+
+    fn deref(&self) -> &Self::Target {
+        &self.dcx
+    }
+}
+
 /// This inner struct exists to keep it all behind a single lock;
 /// this is done to prevent possible deadlocks in a multi-threaded compiler,
 /// as well as inconsistent state observation.
@@ -608,7 +621,7 @@ impl DiagCtxt {
     }
 
     pub fn make_silent(
-        &mut self,
+        &self,
         fallback_bundle: LazyFallbackBundle,
         fatal_note: Option<String>,
         emit_fatal_diagnostic: bool,
@@ -623,7 +636,7 @@ impl DiagCtxt {
         });
     }
 
-    fn wrap_emitter<F>(&mut self, f: F)
+    fn wrap_emitter<F>(&self, f: F)
     where
         F: FnOnce(DiagCtxtInner) -> Box<DynEmitter>,
     {
@@ -738,6 +751,12 @@ impl DiagCtxt {
         *fulfilled_expectations = Default::default();
     }
 
+    pub fn handle<'a>(&'a self) -> DiagCtxtHandle<'a> {
+        DiagCtxtHandle { dcx: self }
+    }
+}
+
+impl<'a> DiagCtxtHandle<'a> {
     /// Stashes a diagnostic for possible later improvement in a different,
     /// later stage of the compiler. Possible actions depend on the diagnostic
     /// level:
@@ -745,8 +764,8 @@ impl DiagCtxt {
     /// - Level::Error: immediately counted as an error that has occurred, because it
     ///   is guaranteed to be emitted eventually. Can be later accessed with the
     ///   provided `span` and `key` through
-    ///   [`DiagCtxt::try_steal_modify_and_emit_err`] or
-    ///   [`DiagCtxt::try_steal_replace_and_emit_err`]. These do not allow
+    ///   [`DiagCtxtHandle::try_steal_modify_and_emit_err`] or
+    ///   [`DiagCtxtHandle::try_steal_replace_and_emit_err`]. These do not allow
     ///   cancellation or downgrading of the error. Returns
     ///   `Some(ErrorGuaranteed)`.
     /// - Level::DelayedBug: this does happen occasionally with errors that are
@@ -757,7 +776,7 @@ impl DiagCtxt {
     ///   user-facing error. Returns `Some(ErrorGuaranteed)` as is normal for
     ///   delayed bugs.
     /// - Level::Warning and lower (i.e. !is_error()): can be accessed with the
-    ///   provided `span` and `key` through [`DiagCtxt::steal_non_err()`]. This
+    ///   provided `span` and `key` through [`DiagCtxtHandle::steal_non_err()`]. This
     ///   allows cancelling and downgrading of the diagnostic. Returns `None`.
     pub fn stash_diagnostic(
         &self,
@@ -793,7 +812,7 @@ impl DiagCtxt {
     /// Steal a previously stashed non-error diagnostic with the given `Span`
     /// and [`StashKey`] as the key. Panics if the found diagnostic is an
     /// error.
-    pub fn steal_non_err(&self, span: Span, key: StashKey) -> Option<Diag<'_, ()>> {
+    pub fn steal_non_err(self, span: Span, key: StashKey) -> Option<Diag<'a, ()>> {
         let key = (span.with_parent(None), key);
         // FIXME(#120456) - is `swap_remove` correct?
         let (diag, guar) = self.inner.borrow_mut().stashed_diagnostics.swap_remove(&key)?;
@@ -807,7 +826,7 @@ impl DiagCtxt {
     /// no matching diagnostic is found. Panics if the found diagnostic's level
     /// isn't `Level::Error`.
     pub fn try_steal_modify_and_emit_err<F>(
-        &self,
+        self,
         span: Span,
         key: StashKey,
         mut modify_err: F,
@@ -833,7 +852,7 @@ impl DiagCtxt {
     /// [`StashKey`] as the key, cancels it if found, and emits `new_err`.
     /// Panics if the found diagnostic's level isn't `Level::Error`.
     pub fn try_steal_replace_and_emit_err(
-        &self,
+        self,
         span: Span,
         key: StashKey,
         new_err: Diag<'_>,
@@ -1106,18 +1125,18 @@ impl DiagCtxt {
 //
 // Functions beginning with `struct_`/`create_` create a diagnostic. Other
 // functions create and emit a diagnostic all in one go.
-impl DiagCtxt {
+impl<'a> DiagCtxtHandle<'a> {
     // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
     // user-facing.
     #[track_caller]
-    pub fn struct_bug(&self, msg: impl Into<Cow<'static, str>>) -> Diag<'_, BugAbort> {
+    pub fn struct_bug(self, msg: impl Into<Cow<'static, str>>) -> Diag<'a, BugAbort> {
         Diag::new(self, Bug, msg.into())
     }
 
     // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
     // user-facing.
     #[track_caller]
-    pub fn bug(&self, msg: impl Into<Cow<'static, str>>) -> ! {
+    pub fn bug(self, msg: impl Into<Cow<'static, str>>) -> ! {
         self.struct_bug(msg).emit()
     }
 
@@ -1125,111 +1144,108 @@ impl DiagCtxt {
     // user-facing.
     #[track_caller]
     pub fn struct_span_bug(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<Cow<'static, str>>,
-    ) -> Diag<'_, BugAbort> {
+    ) -> Diag<'a, BugAbort> {
         self.struct_bug(msg).with_span(span)
     }
 
     // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
     // user-facing.
     #[track_caller]
-    pub fn span_bug(&self, span: impl Into<MultiSpan>, msg: impl Into<Cow<'static, str>>) -> ! {
+    pub fn span_bug(self, span: impl Into<MultiSpan>, msg: impl Into<Cow<'static, str>>) -> ! {
         self.struct_span_bug(span, msg.into()).emit()
     }
 
     #[track_caller]
-    pub fn create_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> {
+    pub fn create_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> Diag<'a, BugAbort> {
         bug.into_diag(self, Bug)
     }
 
     #[track_caller]
-    pub fn emit_bug<'a>(&'a self, bug: impl Diagnostic<'a, BugAbort>) -> ! {
+    pub fn emit_bug(self, bug: impl Diagnostic<'a, BugAbort>) -> ! {
         self.create_bug(bug).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_fatal(&self, msg: impl Into<DiagMessage>) -> Diag<'_, FatalAbort> {
+    pub fn struct_fatal(self, msg: impl Into<DiagMessage>) -> Diag<'a, FatalAbort> {
         Diag::new(self, Fatal, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn fatal(&self, msg: impl Into<DiagMessage>) -> ! {
+    pub fn fatal(self, msg: impl Into<DiagMessage>) -> ! {
         self.struct_fatal(msg).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_span_fatal(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
-    ) -> Diag<'_, FatalAbort> {
+    ) -> Diag<'a, FatalAbort> {
         self.struct_fatal(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn span_fatal(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) -> ! {
+    pub fn span_fatal(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) -> ! {
         self.struct_span_fatal(span, msg).emit()
     }
 
     #[track_caller]
-    pub fn create_fatal<'a>(
-        &'a self,
-        fatal: impl Diagnostic<'a, FatalAbort>,
-    ) -> Diag<'a, FatalAbort> {
+    pub fn create_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> Diag<'a, FatalAbort> {
         fatal.into_diag(self, Fatal)
     }
 
     #[track_caller]
-    pub fn emit_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! {
+    pub fn emit_fatal(self, fatal: impl Diagnostic<'a, FatalAbort>) -> ! {
         self.create_fatal(fatal).emit()
     }
 
     #[track_caller]
-    pub fn create_almost_fatal<'a>(
-        &'a self,
+    pub fn create_almost_fatal(
+        self,
         fatal: impl Diagnostic<'a, FatalError>,
     ) -> Diag<'a, FatalError> {
         fatal.into_diag(self, Fatal)
     }
 
     #[track_caller]
-    pub fn emit_almost_fatal<'a>(&'a self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError {
+    pub fn emit_almost_fatal(self, fatal: impl Diagnostic<'a, FatalError>) -> FatalError {
         self.create_almost_fatal(fatal).emit()
     }
 
     // FIXME: This method should be removed (every error should have an associated error code).
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_err(&self, msg: impl Into<DiagMessage>) -> Diag<'_> {
+    pub fn struct_err(self, msg: impl Into<DiagMessage>) -> Diag<'a> {
         Diag::new(self, Error, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn err(&self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
+    pub fn err(self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
         self.struct_err(msg).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_span_err(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
-    ) -> Diag<'_> {
+    ) -> Diag<'a> {
         self.struct_err(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn span_err(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
     ) -> ErrorGuaranteed {
@@ -1237,12 +1253,12 @@ impl DiagCtxt {
     }
 
     #[track_caller]
-    pub fn create_err<'a>(&'a self, err: impl Diagnostic<'a>) -> Diag<'a> {
+    pub fn create_err(self, err: impl Diagnostic<'a>) -> Diag<'a> {
         err.into_diag(self, Error)
     }
 
     #[track_caller]
-    pub fn emit_err<'a>(&'a self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
+    pub fn emit_err(self, err: impl Diagnostic<'a>) -> ErrorGuaranteed {
         self.create_err(err).emit()
     }
 
@@ -1251,7 +1267,7 @@ impl DiagCtxt {
     // No `#[rustc_lint_diagnostics]` and no `impl Into<DiagMessage>` because bug messages aren't
     // user-facing.
     #[track_caller]
-    pub fn delayed_bug(&self, msg: impl Into<Cow<'static, str>>) -> ErrorGuaranteed {
+    pub fn delayed_bug(self, msg: impl Into<Cow<'static, str>>) -> ErrorGuaranteed {
         Diag::<ErrorGuaranteed>::new(self, DelayedBug, msg.into()).emit()
     }
 
@@ -1264,7 +1280,7 @@ impl DiagCtxt {
     // user-facing.
     #[track_caller]
     pub fn span_delayed_bug(
-        &self,
+        self,
         sp: impl Into<MultiSpan>,
         msg: impl Into<Cow<'static, str>>,
     ) -> ErrorGuaranteed {
@@ -1273,45 +1289,45 @@ impl DiagCtxt {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_warn(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
+    pub fn struct_warn(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
         Diag::new(self, Warning, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn warn(&self, msg: impl Into<DiagMessage>) {
+    pub fn warn(self, msg: impl Into<DiagMessage>) {
         self.struct_warn(msg).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_span_warn(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
-    ) -> Diag<'_, ()> {
+    ) -> Diag<'a, ()> {
         self.struct_warn(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn span_warn(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
+    pub fn span_warn(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
         self.struct_span_warn(span, msg).emit()
     }
 
     #[track_caller]
-    pub fn create_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
+    pub fn create_warn(self, warning: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
         warning.into_diag(self, Warning)
     }
 
     #[track_caller]
-    pub fn emit_warn<'a>(&'a self, warning: impl Diagnostic<'a, ()>) {
+    pub fn emit_warn(self, warning: impl Diagnostic<'a, ()>) {
         self.create_warn(warning).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_note(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
+    pub fn struct_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
         Diag::new(self, Note, msg)
     }
 
@@ -1324,54 +1340,50 @@ impl DiagCtxt {
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_span_note(
-        &self,
+        self,
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagMessage>,
-    ) -> Diag<'_, ()> {
+    ) -> Diag<'a, ()> {
         self.struct_note(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn span_note(&self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
+    pub fn span_note(self, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
         self.struct_span_note(span, msg).emit()
     }
 
     #[track_caller]
-    pub fn create_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
+    pub fn create_note(self, note: impl Diagnostic<'a, ()>) -> Diag<'a, ()> {
         note.into_diag(self, Note)
     }
 
     #[track_caller]
-    pub fn emit_note<'a>(&'a self, note: impl Diagnostic<'a, ()>) {
+    pub fn emit_note(self, note: impl Diagnostic<'a, ()>) {
         self.create_note(note).emit()
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_help(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
+    pub fn struct_help(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
         Diag::new(self, Help, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_failure_note(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
+    pub fn struct_failure_note(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
         Diag::new(self, FailureNote, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_allow(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
+    pub fn struct_allow(self, msg: impl Into<DiagMessage>) -> Diag<'a, ()> {
         Diag::new(self, Allow, msg)
     }
 
     #[rustc_lint_diagnostics]
     #[track_caller]
-    pub fn struct_expect(
-        &self,
-        msg: impl Into<DiagMessage>,
-        id: LintExpectationId,
-    ) -> Diag<'_, ()> {
+    pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
         Diag::new(self, Expect(id), msg)
     }
 }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index b3f6a35f3a4..ddc6490ac0c 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -12,7 +12,7 @@ use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, Item, NodeId, PatKind
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{DiagCtxt, ErrorGuaranteed, PResult};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult};
 use rustc_feature::Features;
 use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools};
 use rustc_parse::{parser::Parser, MACRO_ARGUMENTS};
@@ -1135,7 +1135,7 @@ impl<'a> ExtCtxt<'a> {
         }
     }
 
-    pub fn dcx(&self) -> &'a DiagCtxt {
+    pub fn dcx(&self) -> DiagCtxtHandle<'a> {
         self.sess.dcx()
     }
 
@@ -1256,7 +1256,7 @@ pub fn resolve_path(sess: &Session, path: impl Into<PathBuf>, span: Span) -> PRe
 }
 
 pub fn parse_macro_name_and_helper_attrs(
-    dcx: &rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     attr: &Attribute,
     macro_type: &str,
 ) -> Option<(Symbol, Vec<Symbol>)> {
@@ -1358,7 +1358,7 @@ fn pretty_printing_compatibility_hack(item: &Item, sess: &Session) {
         if crate_matches {
             // FIXME: make this translatable
             #[allow(rustc::untranslatable_diagnostic)]
-            sess.psess.dcx.emit_fatal(errors::ProcMacroBackCompat {
+            sess.dcx().emit_fatal(errors::ProcMacroBackCompat {
                 crate_name: "rental".to_string(),
                 fixed_version: "0.5.6".to_string(),
             });
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index 442fd654b6a..bf475c1dc96 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -7,7 +7,7 @@ use crate::mbe::{
 use rustc_ast::token::{self, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, Diag, DiagCtxt, DiagMessage};
+use rustc_errors::{Applicability, Diag, DiagMessage};
 use rustc_macros::Subdiagnostic;
 use rustc_parse::parser::{Parser, Recovery};
 use rustc_span::source_map::SourceMap;
@@ -61,7 +61,7 @@ pub(super) fn failed_to_match_macro<'cx>(
         err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
     }
 
-    annotate_doc_comment(cx.sess.dcx(), &mut err, psess.source_map(), span);
+    annotate_doc_comment(&mut err, psess.source_map(), span);
 
     if let Some(span) = remaining_matcher.span() {
         err.span_note(span, format!("while trying to match {remaining_matcher}"));
@@ -324,12 +324,12 @@ enum ExplainDocComment {
     },
 }
 
-pub(super) fn annotate_doc_comment(dcx: &DiagCtxt, err: &mut Diag<'_>, sm: &SourceMap, span: Span) {
+pub(super) fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) {
     if let Ok(src) = sm.span_to_snippet(span) {
         if src.starts_with("///") || src.starts_with("/**") {
-            err.subdiagnostic(dcx, ExplainDocComment::Outer { span });
+            err.subdiagnostic(ExplainDocComment::Outer { span });
         } else if src.starts_with("//!") || src.starts_with("/*!") {
-            err.subdiagnostic(dcx, ExplainDocComment::Inner { span });
+            err.subdiagnostic(ExplainDocComment::Inner { span });
         }
     }
 }
diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs
index 72dbbde54b3..d9a945a3215 100644
--- a/compiler/rustc_expand/src/mbe/macro_check.rs
+++ b/compiler/rustc_expand/src/mbe/macro_check.rs
@@ -206,7 +206,7 @@ pub(super) fn check_meta_variables(
     rhses: &[TokenTree],
 ) -> Result<(), ErrorGuaranteed> {
     if lhses.len() != rhses.len() {
-        psess.dcx.span_bug(span, "length mismatch between LHSes and RHSes")
+        psess.dcx().span_bug(span, "length mismatch between LHSes and RHSes")
     }
     let mut guar = None;
     for (lhs, rhs) in iter::zip(lhses, rhses) {
@@ -245,7 +245,7 @@ fn check_binders(
         // MetaVar(fragment) and not as MetaVarDecl(y, fragment).
         TokenTree::MetaVar(span, name) => {
             if macros.is_empty() {
-                psess.dcx.span_bug(span, "unexpected MetaVar in lhs");
+                psess.dcx().span_bug(span, "unexpected MetaVar in lhs");
             }
             let name = MacroRulesNormalizedIdent::new(name);
             // There are 3 possibilities:
@@ -276,7 +276,7 @@ fn check_binders(
                 );
             }
             if !macros.is_empty() {
-                psess.dcx.span_bug(span, "unexpected MetaVarDecl in nested lhs");
+                psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
             }
             let name = MacroRulesNormalizedIdent::new(name);
             if let Some(prev_info) = get_binder_info(macros, binders, name) {
@@ -284,7 +284,7 @@ fn check_binders(
                 // for nested macro definitions.
                 *guar = Some(
                     psess
-                        .dcx
+                        .dcx()
                         .emit_err(errors::DuplicateMatcherBinding { span, prev: prev_info.span }),
                 );
             } else {
@@ -344,7 +344,7 @@ fn check_occurrences(
     match *rhs {
         TokenTree::Token(..) => {}
         TokenTree::MetaVarDecl(span, _name, _kind) => {
-            psess.dcx.span_bug(span, "unexpected MetaVarDecl in rhs")
+            psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs")
         }
         TokenTree::MetaVar(span, name) => {
             let name = MacroRulesNormalizedIdent::new(name);
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index 5d3ba5d3223..49b1f5ce0e3 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -383,7 +383,7 @@ pub fn compile_declarative_macro(
     };
     let dummy_syn_ext = |guar| (mk_syn_ext(Box::new(DummyExpander(guar))), Vec::new());
 
-    let dcx = &sess.psess.dcx;
+    let dcx = sess.dcx();
     let lhs_nm = Ident::new(sym::lhs, def.span);
     let rhs_nm = Ident::new(sym::rhs, def.span);
     let tt_spec = Some(NonterminalKind::TT);
@@ -463,7 +463,7 @@ pub fn compile_declarative_macro(
                 let sp = token.span.substitute_dummy(def.span);
                 let mut err = sess.dcx().struct_span_err(sp, s);
                 err.span_label(sp, msg);
-                annotate_doc_comment(sess.dcx(), &mut err, sess.source_map(), sp);
+                annotate_doc_comment(&mut err, sess.source_map(), sp);
                 let guar = err.emit();
                 return dummy_syn_ext(guar);
             }
diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs
index 3295a91029e..25958e03028 100644
--- a/compiler/rustc_expand/src/mbe/metavar_expr.rs
+++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs
@@ -42,7 +42,7 @@ impl MetaVarExpr {
         let ident = parse_ident(&mut tts, psess, outer_span)?;
         let Some(TokenTree::Delimited(.., Delimiter::Parenthesis, args)) = tts.next() else {
             let msg = "meta-variable expression parameter must be wrapped in parentheses";
-            return Err(psess.dcx.struct_span_err(ident.span, msg));
+            return Err(psess.dcx().struct_span_err(ident.span, msg));
         };
         check_trailing_token(&mut tts, psess)?;
         let mut iter = args.trees();
@@ -62,12 +62,12 @@ impl MetaVarExpr {
                         break;
                     }
                     if !try_eat_comma(&mut iter) {
-                        return Err(psess.dcx.struct_span_err(outer_span, "expected comma"));
+                        return Err(psess.dcx().struct_span_err(outer_span, "expected comma"));
                     }
                 }
                 if result.len() < 2 {
                     return Err(psess
-                        .dcx
+                        .dcx()
                         .struct_span_err(ident.span, "`concat` must have at least two elements"));
                 }
                 MetaVarExpr::Concat(result.into())
@@ -81,7 +81,7 @@ impl MetaVarExpr {
             "len" => MetaVarExpr::Len(parse_depth(&mut iter, psess, ident.span)?),
             _ => {
                 let err_msg = "unrecognized meta-variable expression";
-                let mut err = psess.dcx.struct_span_err(ident.span, err_msg);
+                let mut err = psess.dcx().struct_span_err(ident.span, err_msg);
                 err.span_suggestion(
                     ident.span,
                     "supported expressions are count, ignore, index and len",
@@ -120,7 +120,7 @@ fn check_trailing_token<'psess>(
 ) -> PResult<'psess, ()> {
     if let Some(tt) = iter.next() {
         let mut diag = psess
-            .dcx
+            .dcx()
             .struct_span_err(tt.span(), format!("unexpected token: {}", pprust::tt_to_string(tt)));
         diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens");
         Err(diag)
@@ -139,7 +139,7 @@ fn parse_count<'psess>(
     let ident = parse_ident(iter, psess, span)?;
     let depth = if try_eat_comma(iter) {
         if iter.look_ahead(0).is_none() {
-            return Err(psess.dcx.struct_span_err(
+            return Err(psess.dcx().struct_span_err(
                 span,
                 "`count` followed by a comma must have an associated index indicating its depth",
             ));
@@ -160,7 +160,7 @@ fn parse_depth<'psess>(
     let Some(tt) = iter.next() else { return Ok(0) };
     let TokenTree::Token(token::Token { kind: token::TokenKind::Literal(lit), .. }, _) = tt else {
         return Err(psess
-            .dcx
+            .dcx()
             .struct_span_err(span, "meta-variable expression depth must be a literal"));
     };
     if let Ok(lit_kind) = LitKind::from_token_lit(*lit)
@@ -170,7 +170,7 @@ fn parse_depth<'psess>(
         Ok(n_usize)
     } else {
         let msg = "only unsuffixes integer literals are supported in meta-variable expressions";
-        Err(psess.dcx.struct_span_err(span, msg))
+        Err(psess.dcx().struct_span_err(span, msg))
     }
 }
 
@@ -181,20 +181,21 @@ fn parse_ident<'psess>(
     fallback_span: Span,
 ) -> PResult<'psess, Ident> {
     let Some(tt) = iter.next() else {
-        return Err(psess.dcx.struct_span_err(fallback_span, "expected identifier"));
+        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"));
+        return Err(psess.dcx().struct_span_err(tt.span(), "expected identifier"));
     };
     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));
+            return Err(psess.dcx().struct_span_err(elem.span, RAW_IDENT_ERR));
         }
         return Ok(elem);
     }
     let token_str = pprust::token_to_string(token);
-    let mut err =
-        psess.dcx.struct_span_err(token.span, format!("expected identifier, found `{token_str}`"));
+    let mut err = psess
+        .dcx()
+        .struct_span_err(token.span, format!("expected identifier, found `{token_str}`"));
     err.span_suggestion(
         token.span,
         format!("try removing `{token_str}`"),
@@ -236,7 +237,7 @@ fn eat_dollar<'psess>(
         let _ = iter.next();
         return Ok(());
     }
-    Err(psess.dcx.struct_span_err(
+    Err(psess.dcx().struct_span_err(
         span,
         "meta-variables within meta-variable expressions must be referenced using a dollar sign",
     ))
diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs
index 914bd03675a..f935f1b77e0 100644
--- a/compiler/rustc_expand/src/mbe/transcribe.rs
+++ b/compiler/rustc_expand/src/mbe/transcribe.rs
@@ -10,7 +10,7 @@ use rustc_ast::token::IdentIsRaw;
 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, DiagCtxt, PResult};
+use rustc_errors::{pluralize, Diag, DiagCtxtHandle, PResult};
 use rustc_parse::parser::ParseNtResult;
 use rustc_session::parse::ParseSess;
 use rustc_span::hygiene::{LocalExpnId, Transparency};
@@ -141,7 +141,7 @@ pub(super) fn transcribe<'a>(
     let mut result_stack = Vec::new();
     let mut marker = Marker(expand_id, transparency, Default::default());
 
-    let dcx = &psess.dcx;
+    let dcx = psess.dcx();
     loop {
         // Look at the last frame on the stack.
         // If it still has a TokenTree we have not looked at yet, use that tree.
@@ -571,7 +571,7 @@ fn lockstep_iter_size(
 /// * `[ $( ${count(foo, 1)} ),* ]` will return an error because `${count(foo, 1)}` is
 ///   declared inside a single repetition and the index `1` implies two nested repetitions.
 fn count_repetitions<'a>(
-    dcx: &'a DiagCtxt,
+    dcx: DiagCtxtHandle<'a>,
     depth_user: usize,
     mut matched: &NamedMatch,
     repeats: &[(usize, usize)],
@@ -632,7 +632,7 @@ fn count_repetitions<'a>(
 
 /// Returns a `NamedMatch` item declared on the LHS given an arbitrary [Ident]
 fn matched_from_ident<'ctx, 'interp, 'rslt>(
-    dcx: &'ctx DiagCtxt,
+    dcx: DiagCtxtHandle<'ctx>,
     ident: Ident,
     interp: &'interp FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
 ) -> PResult<'ctx, &'rslt NamedMatch>
@@ -646,7 +646,7 @@ where
 
 /// Used by meta-variable expressions when an user input is out of the actual declared bounds. For
 /// example, index(999999) in an repetition of only three elements.
-fn out_of_bounds_err<'a>(dcx: &'a DiagCtxt, max: usize, span: Span, ty: &str) -> Diag<'a> {
+fn out_of_bounds_err<'a>(dcx: DiagCtxtHandle<'a>, max: usize, span: Span, ty: &str) -> Diag<'a> {
     let msg = if max == 0 {
         format!(
             "meta-variable expression `{ty}` with depth parameter \
@@ -662,7 +662,7 @@ fn out_of_bounds_err<'a>(dcx: &'a DiagCtxt, max: usize, span: Span, ty: &str) ->
 }
 
 fn transcribe_metavar_expr<'a>(
-    dcx: &'a DiagCtxt,
+    dcx: DiagCtxtHandle<'a>,
     expr: &MetaVarExpr,
     interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
     marker: &mut Marker,
@@ -730,7 +730,7 @@ fn transcribe_metavar_expr<'a>(
 
 /// Extracts an identifier that can be originated from a `$var:ident` variable or from a token tree.
 fn extract_ident<'a>(
-    dcx: &'a DiagCtxt,
+    dcx: DiagCtxtHandle<'a>,
     ident: Ident,
     interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
 ) -> PResult<'a, String> {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 93f8682090d..5508358f53b 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -522,7 +522,7 @@ impl server::FreeFunctions for Rustc<'_, '_> {
     fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
         let message = rustc_errors::DiagMessage::from(diagnostic.message);
         let mut diag: Diag<'_, ()> =
-            Diag::new(&self.psess().dcx, diagnostic.level.to_internal(), message);
+            Diag::new(self.psess().dcx(), diagnostic.level.to_internal(), message);
         diag.span(MultiSpan::from_spans(diagnostic.spans));
         for child in diagnostic.children {
             // This message comes from another diagnostic, and we are just reconstructing the
diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs
index 58832cb1087..066a5a8f046 100644
--- a/compiler/rustc_feature/src/unstable.rs
+++ b/compiler/rustc_feature/src/unstable.rs
@@ -567,8 +567,8 @@ declare_features! (
     (unstable, optimize_attribute, "1.34.0", Some(54882)),
     /// Allows postfix match `expr.match { ... }`
     (unstable, postfix_match, "1.79.0", Some(121618)),
-    /// Allows `use<'a, 'b, A, B>` in `impl use<...> Trait` for precise capture of generic args.
-    (incomplete, precise_capturing, "1.79.0", Some(123432)),
+    /// Allows `use<'a, 'b, A, B>` in `impl Trait + use<...>` for precise capture of generic args.
+    (unstable, precise_capturing, "1.79.0", Some(123432)),
     /// Allows macro attributes on expressions, statements and non-inline modules.
     (unstable, proc_macro_hygiene, "1.30.0", Some(54727)),
     /// Allows `&raw const $place_expr` and `&raw mut $place_expr` expressions.
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index d4a22c4c31f..22a6c06bba3 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -463,6 +463,7 @@ pub enum TraitBoundModifier {
 pub enum GenericBound<'hir> {
     Trait(PolyTraitRef<'hir>, TraitBoundModifier),
     Outlives(&'hir Lifetime),
+    Use(&'hir [PreciseCapturingArg<'hir>], Span),
 }
 
 impl GenericBound<'_> {
@@ -477,6 +478,7 @@ impl GenericBound<'_> {
         match self {
             GenericBound::Trait(t, ..) => t.span,
             GenericBound::Outlives(l) => l.ident.span,
+            GenericBound::Use(_, span) => *span,
         }
     }
 }
@@ -2689,8 +2691,6 @@ pub struct OpaqueTy<'hir> {
     /// originating from a trait method. This makes it so that the opaque is
     /// lowered as an associated type.
     pub in_trait: bool,
-    /// List of arguments captured via `impl use<'a, P, ...> Trait` syntax.
-    pub precise_capturing_args: Option<(&'hir [PreciseCapturingArg<'hir>], Span)>,
 }
 
 #[derive(Debug, Clone, Copy, HashStable_Generic)]
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 5a16f266dab..065ecc5d7b7 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -532,15 +532,10 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) -> V::
             try_visit!(visitor.visit_ty(ty));
             try_visit!(visitor.visit_generics(generics));
         }
-        ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, precise_capturing_args, .. }) => {
+        ItemKind::OpaqueTy(&OpaqueTy { generics, bounds, .. }) => {
             try_visit!(visitor.visit_id(item.hir_id()));
             try_visit!(walk_generics(visitor, generics));
             walk_list!(visitor, visit_param_bound, bounds);
-            if let Some((precise_capturing_args, _)) = precise_capturing_args {
-                for arg in precise_capturing_args {
-                    try_visit!(visitor.visit_precise_capturing_arg(arg));
-                }
-            }
         }
         ItemKind::Enum(ref enum_definition, ref generics) => {
             try_visit!(visitor.visit_generics(generics));
@@ -1147,6 +1142,10 @@ pub fn walk_param_bound<'v, V: Visitor<'v>>(
     match *bound {
         GenericBound::Trait(ref typ, _modifier) => visitor.visit_poly_trait_ref(typ),
         GenericBound::Outlives(ref lifetime) => visitor.visit_lifetime(lifetime),
+        GenericBound::Use(args, _) => {
+            walk_list!(visitor, visit_precise_capturing_arg, args);
+            V::Result::output()
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 72e5995e892..3b53c253195 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -481,9 +481,12 @@ fn sanity_check_found_hidden_type<'tcx>(
 /// 2. Checking that all lifetimes that are implicitly captured are mentioned.
 /// 3. Asserting that all parameters mentioned in the captures list are invariant.
 fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) {
-    let hir::OpaqueTy { precise_capturing_args, .. } =
+    let hir::OpaqueTy { bounds, .. } =
         *tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty();
-    let Some((precise_capturing_args, _)) = precise_capturing_args else {
+    let Some(precise_capturing_args) = bounds.iter().find_map(|bound| match *bound {
+        hir::GenericBound::Use(bounds, ..) => Some(bounds),
+        _ => None,
+    }) else {
         // No precise capturing args; nothing to validate
         return;
     };
diff --git a/compiler/rustc_hir_analysis/src/check/errs.rs b/compiler/rustc_hir_analysis/src/check/errs.rs
index 2cdcc06f53c..17cb20df754 100644
--- a/compiler/rustc_hir_analysis/src/check/errs.rs
+++ b/compiler/rustc_hir_analysis/src/check/errs.rs
@@ -63,7 +63,7 @@ fn handle_static_mut_ref(
         } else {
             (errors::StaticMutRefSugg::Shared { span, var }, "shared")
         };
-        tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared });
+        tcx.dcx().emit_err(errors::StaticMutRef { span, sugg, shared });
     } else {
         let (sugg, shared) = if mutable == Mutability::Mut {
             (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 61ac4af0151..9421269e51e 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -403,74 +403,56 @@ fn emit_orphan_check_error<'tcx>(
                 match *ty.kind() {
                     ty::Slice(_) => {
                         if is_foreign {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsForeign { span },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span });
                         } else {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsName { span, name: "slices" },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsName {
+                                span,
+                                name: "slices",
+                            });
                         }
                     }
                     ty::Array(..) => {
                         if is_foreign {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsForeign { span },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span });
                         } else {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsName { span, name: "arrays" },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsName {
+                                span,
+                                name: "arrays",
+                            });
                         }
                     }
                     ty::Tuple(..) => {
                         if is_foreign {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsForeign { span },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsForeign { span });
                         } else {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsName { span, name: "tuples" },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsName {
+                                span,
+                                name: "tuples",
+                            });
                         }
                     }
                     ty::Alias(ty::Opaque, ..) => {
-                        diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsOpaque { span });
+                        diag.subdiagnostic(errors::OnlyCurrentTraitsOpaque { span });
                     }
                     ty::RawPtr(ptr_ty, mutbl) => {
                         if !trait_ref.self_ty().has_param() {
-                            diag.subdiagnostic(
-                                tcx.dcx(),
-                                errors::OnlyCurrentTraitsPointerSugg {
-                                    wrapper_span: impl_.self_ty.span,
-                                    struct_span: item.span.shrink_to_lo(),
-                                    mut_key: mutbl.prefix_str(),
-                                    ptr_ty,
-                                },
-                            );
+                            diag.subdiagnostic(errors::OnlyCurrentTraitsPointerSugg {
+                                wrapper_span: impl_.self_ty.span,
+                                struct_span: item.span.shrink_to_lo(),
+                                mut_key: mutbl.prefix_str(),
+                                ptr_ty,
+                            });
                         }
-                        diag.subdiagnostic(
-                            tcx.dcx(),
-                            errors::OnlyCurrentTraitsPointer { span, pointer: ty },
-                        );
+                        diag.subdiagnostic(errors::OnlyCurrentTraitsPointer { span, pointer: ty });
                     }
                     ty::Adt(adt_def, _) => {
-                        diag.subdiagnostic(
-                            tcx.dcx(),
-                            errors::OnlyCurrentTraitsAdt {
-                                span,
-                                name: tcx.def_path_str(adt_def.did()),
-                            },
-                        );
+                        diag.subdiagnostic(errors::OnlyCurrentTraitsAdt {
+                            span,
+                            name: tcx.def_path_str(adt_def.did()),
+                        });
                     }
                     _ => {
-                        diag.subdiagnostic(tcx.dcx(), errors::OnlyCurrentTraitsTy { span, ty });
+                        diag.subdiagnostic(errors::OnlyCurrentTraitsTy { span, ty });
                     }
                 }
             }
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index 41dceea2e32..cff8d5a5ea5 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -2,7 +2,7 @@
 
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan,
+    codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
@@ -424,7 +424,7 @@ pub struct MissingTypeParams {
 // Manual implementation of `Diagnostic` to be able to call `span_to_snippet`.
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for MissingTypeParams {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let mut err = Diag::new(dcx, level, fluent::hir_analysis_missing_type_params);
         err.span(self.span);
         err.code(E0393);
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 9a6bc8a7b72..c7699b0b310 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
@@ -178,6 +178,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
                         lifetime.ident.span,
                     );
                 }
+                hir::GenericBound::Use(..) => {
+                    // We don't actually lower `use` into the type layer.
+                }
             }
         }
     }
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index d32d0183c4e..b21f1eadfb7 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -13,7 +13,7 @@ use rustc_ast_pretty::pprust::{Comments, PrintState};
 use rustc_hir as hir;
 use rustc_hir::{
     BindingMode, ByRef, GenericArg, GenericBound, GenericParam, GenericParamKind, HirId,
-    LifetimeParamKind, Node, PatKind, RangeEnd, Term, TraitBoundModifier,
+    LifetimeParamKind, Node, PatKind, PreciseCapturingArg, RangeEnd, Term, TraitBoundModifier,
 };
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, Ident, Symbol};
@@ -2100,10 +2100,24 @@ impl<'a> State<'a> {
                 GenericBound::Outlives(lt) => {
                     self.print_lifetime(lt);
                 }
+                GenericBound::Use(args, _) => {
+                    self.word("use <");
+
+                    self.commasep(Inconsistent, args, |s, arg| s.print_precise_capturing_arg(*arg));
+
+                    self.word(">");
+                }
             }
         }
     }
 
+    fn print_precise_capturing_arg(&mut self, arg: PreciseCapturingArg<'_>) {
+        match arg {
+            PreciseCapturingArg::Lifetime(lt) => self.print_lifetime(lt),
+            PreciseCapturingArg::Param(arg) => self.print_ident(arg.ident),
+        }
+    }
+
     fn print_generic_params(&mut self, generic_params: &[GenericParam<'_>]) {
         if !generic_params.is_empty() {
             self.word("<");
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 281af80bff5..4e2104ff561 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -246,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let semi = expr.span.shrink_to_hi().with_hi(semi_span.hi());
         let sugg = crate::errors::RemoveSemiForCoerce { expr: expr.span, ret, semi };
-        diag.subdiagnostic(self.dcx(), sugg);
+        diag.subdiagnostic(sugg);
     }
 
     /// When the previously checked expression (the scrutinee) diverges,
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 9e9a1f678ed..58708510282 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -1005,25 +1005,19 @@ impl<'a, 'tcx> CastCheck<'tcx> {
             if let Some((deref_ty, _)) = derefed {
                 // Give a note about what the expr derefs to.
                 if deref_ty != self.expr_ty.peel_refs() {
-                    err.subdiagnostic(
-                        fcx.dcx(),
-                        errors::DerefImplsIsEmpty {
-                            span: self.expr_span,
-                            deref_ty: fcx.ty_to_string(deref_ty),
-                        },
-                    );
+                    err.subdiagnostic(errors::DerefImplsIsEmpty {
+                        span: self.expr_span,
+                        deref_ty: fcx.ty_to_string(deref_ty),
+                    });
                 }
 
                 // Create a multipart suggestion: add `!` and `.is_empty()` in
                 // place of the cast.
-                err.subdiagnostic(
-                    fcx.dcx(),
-                    errors::UseIsEmpty {
-                        lo: self.expr_span.shrink_to_lo(),
-                        hi: self.span.with_lo(self.expr_span.hi()),
-                        expr_ty: fcx.ty_to_string(self.expr_ty),
-                    },
-                );
+                err.subdiagnostic(errors::UseIsEmpty {
+                    lo: self.expr_span.shrink_to_lo(),
+                    hi: self.span.with_lo(self.expr_span.hi()),
+                    expr_ty: fcx.ty_to_string(self.expr_ty),
+                });
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index fcd22b74676..31f85e21d71 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -1782,20 +1782,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         }
 
         let rpid_def_span = fcx.tcx.def_span(rpit_def_id);
-        err.subdiagnostic(
-            fcx.tcx.dcx(),
-            SuggestBoxingForReturnImplTrait::ChangeReturnType {
-                start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)),
-                end_sp: rpid_def_span.shrink_to_hi(),
-            },
-        );
+        err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType {
+            start_sp: rpid_def_span.with_hi(rpid_def_span.lo() + BytePos(4)),
+            end_sp: rpid_def_span.shrink_to_hi(),
+        });
 
         let (starts, ends) =
             arm_spans.map(|span| (span.shrink_to_lo(), span.shrink_to_hi())).unzip();
-        err.subdiagnostic(
-            fcx.tcx.dcx(),
-            SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends },
-        );
+        err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { starts, ends });
     }
 
     fn report_return_mismatched_types<'a>(
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index fe497498c4b..233dc2afa9b 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -384,7 +384,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         if let Some(sp) =
                             tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp)
                         {
-                            err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+                            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
                         }
                         oprnd_t = Ty::new_error(tcx, err.emit());
                     }
@@ -2018,10 +2018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .shrink_to_hi()
                 .to(range_end.span);
 
-            err.subdiagnostic(
-                self.dcx(),
-                TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr },
-            );
+            err.subdiagnostic(TypeMismatchFruTypo { expr_span: range_start.span, fru_span, expr });
 
             // Suppress any range expr type mismatches
             self.dcx().try_steal_replace_and_emit_err(
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 10d9e07db6f..1138642c56d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1435,7 +1435,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             // The user provided `ptr::null()`, but the function expects
             // `ptr::null_mut()`.
-            err.subdiagnostic(self.dcx(), SuggestPtrNullMut { span: arg.span });
+            err.subdiagnostic(SuggestPtrNullMut { span: arg.span });
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 2ef27e6a0ba..1713d75092e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -5,14 +5,13 @@ mod checks;
 mod inspect_obligations;
 mod suggestions;
 
-use rustc_errors::ErrorGuaranteed;
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
 
 use crate::coercion::DynamicCoerceMany;
 use crate::fallback::DivergingFallbackBehavior;
 use crate::fn_ctxt::checks::DivergingBlockBehavior;
 use crate::{CoroutineTypes, Diverges, EnclosingBreakables, TypeckRootCtxt};
 use hir::def_id::CRATE_DEF_ID;
-use rustc_errors::DiagCtxt;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, RegionInferReason};
@@ -145,8 +144,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    pub(crate) fn dcx(&self) -> &'tcx DiagCtxt {
-        self.tcx.dcx()
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {
+        self.infcx.dcx()
     }
 
     pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> {
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index 9743dc7c69f..337a92c0d01 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -460,16 +460,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // but those checks need to be a bit more delicate and the benefit is diminishing.
             if self.can_eq(self.param_env, found_ty_inner, peeled) && error_tys_equate_as_ref {
                 let sugg = prefix_wrap(".as_ref()");
-                err.subdiagnostic(
-                    self.dcx(),
-                    errors::SuggestConvertViaMethod {
-                        span: expr.span.shrink_to_hi(),
-                        sugg,
-                        expected,
-                        found,
-                        borrow_removal_span,
-                    },
-                );
+                err.subdiagnostic(errors::SuggestConvertViaMethod {
+                    span: expr.span.shrink_to_hi(),
+                    sugg,
+                    expected,
+                    found,
+                    borrow_removal_span,
+                });
                 return true;
             } else if let Some((deref_ty, _)) =
                 self.autoderef(expr.span, found_ty_inner).silence_errors().nth(1)
@@ -477,16 +474,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 && error_tys_equate_as_ref
             {
                 let sugg = prefix_wrap(".as_deref()");
-                err.subdiagnostic(
-                    self.dcx(),
-                    errors::SuggestConvertViaMethod {
-                        span: expr.span.shrink_to_hi(),
-                        sugg,
-                        expected,
-                        found,
-                        borrow_removal_span,
-                    },
-                );
+                err.subdiagnostic(errors::SuggestConvertViaMethod {
+                    span: expr.span.shrink_to_hi(),
+                    sugg,
+                    expected,
+                    found,
+                    borrow_removal_span,
+                });
                 return true;
             } else if let ty::Adt(adt, _) = found_ty_inner.peel_refs().kind()
                 && self.tcx.is_lang_item(adt.did(), LangItem::String)
@@ -573,7 +567,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     end: span.shrink_to_hi(),
                 },
             };
-            err.subdiagnostic(self.dcx(), suggest_boxing);
+            err.subdiagnostic(suggest_boxing);
 
             true
         } else {
@@ -814,28 +808,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match &fn_decl.output {
             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => {
                 // `fn main()` must return `()`, do not suggest changing return type
-                err.subdiagnostic(self.dcx(), errors::ExpectedReturnTypeLabel::Unit { span });
+                err.subdiagnostic(errors::ExpectedReturnTypeLabel::Unit { span });
                 return true;
             }
             &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
                 if let Some(found) = found.make_suggestable(self.tcx, false, None) {
-                    err.subdiagnostic(
-                        self.dcx(),
-                        errors::AddReturnTypeSuggestion::Add { span, found: found.to_string() },
-                    );
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
+                        span,
+                        found: found.to_string(),
+                    });
                     return true;
                 } else if let Some(sugg) = suggest_impl_trait(self, self.param_env, found) {
-                    err.subdiagnostic(
-                        self.dcx(),
-                        errors::AddReturnTypeSuggestion::Add { span, found: sugg },
-                    );
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::Add { span, found: sugg });
                     return true;
                 } else {
                     // FIXME: if `found` could be `impl Iterator` we should suggest that.
-                    err.subdiagnostic(
-                        self.dcx(),
-                        errors::AddReturnTypeSuggestion::MissingHere { span },
-                    );
+                    err.subdiagnostic(errors::AddReturnTypeSuggestion::MissingHere { span });
                     return true;
                 }
             }
@@ -856,19 +844,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     debug!(?found);
                     if found.is_suggestable(self.tcx, false) {
                         if ty.span.is_empty() {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                errors::AddReturnTypeSuggestion::Add {
-                                    span: ty.span,
-                                    found: found.to_string(),
-                                },
-                            );
+                            err.subdiagnostic(errors::AddReturnTypeSuggestion::Add {
+                                span: ty.span,
+                                found: found.to_string(),
+                            });
                             return true;
                         } else {
-                            err.subdiagnostic(
-                                self.dcx(),
-                                errors::ExpectedReturnTypeLabel::Other { span: ty.span, expected },
-                            );
+                            err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
+                                span: ty.span,
+                                expected,
+                            });
                         }
                     }
                 } else {
@@ -883,10 +868,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     let ty = self.normalize(hir_ty.span, ty);
                     let ty = self.tcx.instantiate_bound_regions_with_erased(ty);
                     if self.can_coerce(expected, ty) {
-                        err.subdiagnostic(
-                            self.dcx(),
-                            errors::ExpectedReturnTypeLabel::Other { span: hir_ty.span, expected },
-                        );
+                        err.subdiagnostic(errors::ExpectedReturnTypeLabel::Other {
+                            span: hir_ty.span,
+                            expected,
+                        });
                         self.try_suggest_return_impl_trait(err, expected, found, fn_id);
                         self.note_caller_chooses_ty_for_ty_param(err, expected, found);
                         return true;
@@ -905,13 +890,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         found: Ty<'tcx>,
     ) {
         if let ty::Param(expected_ty_as_param) = expected.kind() {
-            diag.subdiagnostic(
-                self.dcx(),
-                errors::NoteCallerChoosesTyForTyParam {
-                    ty_param_name: expected_ty_as_param.name,
-                    found_ty: found,
-                },
-            );
+            diag.subdiagnostic(errors::NoteCallerChoosesTyForTyParam {
+                ty_param_name: expected_ty_as_param.name,
+                found_ty: found,
+            });
         }
     }
 
@@ -1136,7 +1118,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
         if let Some(sp) = self.tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
             // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
-            err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
             true
         } else {
             false
@@ -1250,7 +1232,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 } else {
                     return false;
                 };
-                diag.subdiagnostic(self.dcx(), subdiag);
+                diag.subdiagnostic(subdiag);
                 return true;
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index abbfe452f5f..ff066bb9ede 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -2788,32 +2788,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         errors: Vec<FulfillmentError<'tcx>>,
         suggest_derive: bool,
     ) {
-        let all_local_types_needing_impls =
-            errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() {
+        let preds: Vec<_> = errors
+            .iter()
+            .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
                 ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
                     match pred.self_ty().kind() {
-                        ty::Adt(def, _) => def.did().is_local(),
-                        _ => false,
+                        ty::Adt(_, _) => Some(pred),
+                        _ => None,
                     }
                 }
-                _ => false,
-            });
-        let mut preds: Vec<_> = errors
-            .iter()
-            .filter_map(|e| match e.obligation.predicate.kind().skip_binder() {
-                ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => Some(pred),
                 _ => None,
             })
             .collect();
-        preds.sort_by_key(|pred| pred.trait_ref.to_string());
-        let def_ids = preds
+
+        // Note for local items and foreign items respectively.
+        let (mut local_preds, mut foreign_preds): (Vec<_>, Vec<_>) =
+            preds.iter().partition(|&pred| {
+                if let ty::Adt(def, _) = pred.self_ty().kind() {
+                    def.did().is_local()
+                } else {
+                    false
+                }
+            });
+
+        local_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
+        let local_def_ids = local_preds
             .iter()
             .filter_map(|pred| match pred.self_ty().kind() {
                 ty::Adt(def, _) => Some(def.did()),
                 _ => None,
             })
             .collect::<FxIndexSet<_>>();
-        let mut spans: MultiSpan = def_ids
+        let mut local_spans: MultiSpan = local_def_ids
             .iter()
             .filter_map(|def_id| {
                 let span = self.tcx.def_span(*def_id);
@@ -2821,11 +2827,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             })
             .collect::<Vec<_>>()
             .into();
-
-        for pred in &preds {
+        for pred in &local_preds {
             match pred.self_ty().kind() {
-                ty::Adt(def, _) if def.did().is_local() => {
-                    spans.push_span_label(
+                ty::Adt(def, _) => {
+                    local_spans.push_span_label(
                         self.tcx.def_span(def.did()),
                         format!("must implement `{}`", pred.trait_ref.print_trait_sugared()),
                     );
@@ -2833,24 +2838,69 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => {}
             }
         }
-
-        if all_local_types_needing_impls && spans.primary_span().is_some() {
-            let msg = if preds.len() == 1 {
+        if local_spans.primary_span().is_some() {
+            let msg = if local_preds.len() == 1 {
                 format!(
                     "an implementation of `{}` might be missing for `{}`",
-                    preds[0].trait_ref.print_trait_sugared(),
-                    preds[0].self_ty()
+                    local_preds[0].trait_ref.print_trait_sugared(),
+                    local_preds[0].self_ty()
                 )
             } else {
                 format!(
                     "the following type{} would have to `impl` {} required trait{} for this \
                      operation to be valid",
-                    pluralize!(def_ids.len()),
-                    if def_ids.len() == 1 { "its" } else { "their" },
-                    pluralize!(preds.len()),
+                    pluralize!(local_def_ids.len()),
+                    if local_def_ids.len() == 1 { "its" } else { "their" },
+                    pluralize!(local_preds.len()),
+                )
+            };
+            err.span_note(local_spans, msg);
+        }
+
+        foreign_preds.sort_by_key(|pred: &&ty::TraitPredicate<'_>| pred.trait_ref.to_string());
+        let foreign_def_ids = foreign_preds
+            .iter()
+            .filter_map(|pred| match pred.self_ty().kind() {
+                ty::Adt(def, _) => Some(def.did()),
+                _ => None,
+            })
+            .collect::<FxIndexSet<_>>();
+        let mut foreign_spans: MultiSpan = foreign_def_ids
+            .iter()
+            .filter_map(|def_id| {
+                let span = self.tcx.def_span(*def_id);
+                if span.is_dummy() { None } else { Some(span) }
+            })
+            .collect::<Vec<_>>()
+            .into();
+        for pred in &foreign_preds {
+            match pred.self_ty().kind() {
+                ty::Adt(def, _) => {
+                    foreign_spans.push_span_label(
+                        self.tcx.def_span(def.did()),
+                        format!("not implement `{}`", pred.trait_ref.print_trait_sugared()),
+                    );
+                }
+                _ => {}
+            }
+        }
+        if foreign_spans.primary_span().is_some() {
+            let msg = if foreign_preds.len() == 1 {
+                format!(
+                    "the foreign item type `{}` doesn't implement `{}`",
+                    foreign_preds[0].self_ty(),
+                    foreign_preds[0].trait_ref.print_trait_sugared()
+                )
+            } else {
+                format!(
+                    "the foreign item type{} {} implement required trait{} for this \
+                     operation to be valid",
+                    pluralize!(foreign_def_ids.len()),
+                    if foreign_def_ids.len() > 1 { "don't" } else { "doesn't" },
+                    pluralize!(foreign_preds.len()),
                 )
             };
-            err.span_note(spans, msg);
+            err.span_note(foreign_spans, msg);
         }
 
         let preds: Vec<_> = errors
@@ -3729,22 +3779,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if impls_trait(trait_info.def_id) {
                         self.suggest_valid_traits(err, item_name, vec![trait_info.def_id], false);
                     } else {
-                        err.subdiagnostic(
-                            self.dcx(),
-                            CandidateTraitNote {
-                                span: self.tcx.def_span(trait_info.def_id),
-                                trait_name: self.tcx.def_path_str(trait_info.def_id),
-                                item_name,
-                                action_or_ty: if trait_missing_method {
-                                    "NONE".to_string()
-                                } else {
-                                    param_type.map_or_else(
-                                        || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
-                                        |p| p.to_string(),
-                                    )
-                                },
+                        err.subdiagnostic(CandidateTraitNote {
+                            span: self.tcx.def_span(trait_info.def_id),
+                            trait_name: self.tcx.def_path_str(trait_info.def_id),
+                            item_name,
+                            action_or_ty: if trait_missing_method {
+                                "NONE".to_string()
+                            } else {
+                                param_type.map_or_else(
+                                    || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
+                                    |p| p.to_string(),
+                                )
                             },
-                        );
+                        });
                     }
                 }
                 trait_infos => {
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index d774ae2146a..5a11cb7096f 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -820,7 +820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // If the previous expression was a block expression, suggest parentheses
                         // (turning this into a binary subtraction operation instead.)
                         // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs)
-                        err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
                     } else {
                         match actual.kind() {
                             Uint(_) if op == hir::UnOp::Neg => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index e15866f3f0f..227691d0994 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -61,8 +61,8 @@ 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, DiagCtxt, DiagStyledString,
-    ErrorGuaranteed, IntoDiagArg, StringPart,
+    codes::*, pluralize, struct_span_code_err, Applicability, Diag, DiagCtxtHandle,
+    DiagStyledString, ErrorGuaranteed, IntoDiagArg, StringPart,
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -139,8 +139,8 @@ pub struct TypeErrCtxt<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
-    pub fn dcx(&self) -> &'tcx DiagCtxt {
-        self.infcx.tcx.dcx()
+    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
+        self.infcx.dcx()
     }
 
     /// This is just to avoid a potential footgun of accidentally
@@ -892,7 +892,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         arm_ty,
                         arm_span,
                     ) {
-                        err.subdiagnostic(self.dcx(), subdiag);
+                        err.subdiagnostic(subdiag);
                     }
                 }
             },
@@ -918,7 +918,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     else_ty,
                     else_span,
                 ) {
-                    err.subdiagnostic(self.dcx(), subdiag);
+                    err.subdiagnostic(subdiag);
                 }
             }
             ObligationCauseCode::LetElse => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index acb74f8a82c..8fd19563c30 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -369,7 +369,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 trait_predicates: trait_predicates.join(", "),
             }
         };
-        err.subdiagnostic(self.dcx(), suggestion);
+        err.subdiagnostic(suggestion);
     }
 
     pub(super) fn report_placeholder_failure(
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 17fb760295a..74c65e93616 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -121,7 +121,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             span_low: cause.span.shrink_to_lo(),
                             span_high: cause.span.shrink_to_hi(),
                         };
-                        diag.subdiagnostic(self.dcx(), sugg);
+                        diag.subdiagnostic(sugg);
                     }
                     _ => {
                         // More than one matching variant.
@@ -130,7 +130,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             cause_span: cause.span,
                             compatible_variants,
                         };
-                        diag.subdiagnostic(self.dcx(), sugg);
+                        diag.subdiagnostic(sugg);
                     }
                 }
             }
@@ -202,10 +202,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             },
             (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
                 // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
-                diag.subdiagnostic(
-                    self.dcx(),
-                    ConsiderAddingAwait::FutureSugg { span: exp_span.shrink_to_hi() },
-                );
+                diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
+                    span: exp_span.shrink_to_hi(),
+                });
                 Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
             }
             (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
@@ -233,7 +232,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             _ => None,
         };
         if let Some(subdiag) = subdiag {
-            diag.subdiagnostic(self.dcx(), subdiag);
+            diag.subdiagnostic(subdiag);
         }
     }
 
@@ -269,7 +268,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         } else {
                             return;
                         };
-                        diag.subdiagnostic(self.dcx(), suggestion);
+                        diag.subdiagnostic(suggestion);
                     }
                 }
             }
@@ -401,15 +400,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
                     (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
                     (true, true) => {
-                        diag.subdiagnostic(self.dcx(), FnItemsAreDistinct);
+                        diag.subdiagnostic(FnItemsAreDistinct);
                         FunctionPointerSuggestion::CastRef { span, fn_name, sig: *sig }
                     }
                     (false, false) => {
-                        diag.subdiagnostic(self.dcx(), FnItemsAreDistinct);
+                        diag.subdiagnostic(FnItemsAreDistinct);
                         FunctionPointerSuggestion::Cast { span, fn_name, sig: *sig }
                     }
                 };
-                diag.subdiagnostic(self.dcx(), sugg);
+                diag.subdiagnostic(sugg);
             }
             (ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
                 let expected_sig =
@@ -418,7 +417,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     &(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
 
                 if self.same_type_modulo_infer(*expected_sig, *found_sig) {
-                    diag.subdiagnostic(self.dcx(), FnUniqTypes);
+                    diag.subdiagnostic(FnUniqTypes);
                 }
 
                 if !self.same_type_modulo_infer(*found_sig, *expected_sig)
@@ -447,7 +446,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     }
                 };
 
-                diag.subdiagnostic(self.dcx(), sug);
+                diag.subdiagnostic(sug);
             }
             (ty::FnDef(did, args), ty::FnPtr(sig)) => {
                 let expected_sig =
@@ -466,7 +465,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     format!("{fn_name} as {found_sig}")
                 };
 
-                diag.subdiagnostic(self.dcx(), FnConsiderCasting { casting });
+                diag.subdiagnostic(FnConsiderCasting { casting });
             }
             _ => {
                 return;
@@ -889,7 +888,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let diag = self.consider_returning_binding_diag(blk, expected_ty);
         match diag {
             Some(diag) => {
-                err.subdiagnostic(self.dcx(), diag);
+                err.subdiagnostic(diag);
                 true
             }
             None => false,
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 8412912b9f3..510e9a06dfb 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -4,13 +4,14 @@ pub use lexical_region_resolve::RegionResolutionError;
 pub use relate::combine::CombineFields;
 pub use relate::combine::PredicateEmittingRelation;
 pub use relate::StructurallyRelateAliases;
+use rustc_errors::DiagCtxtHandle;
 pub use rustc_macros::{TypeFoldable, TypeVisitable};
 pub use rustc_middle::ty::IntVarValue;
 pub use BoundRegionConversionTime::*;
 pub use RegionVariableOrigin::*;
 pub use SubregionOrigin::*;
 
-use crate::infer::relate::{Relate, RelateResult};
+use crate::infer::relate::RelateResult;
 use crate::traits::{self, ObligationCause, ObligationInspector, PredicateObligation, TraitEngine};
 use error_reporting::TypeErrCtxt;
 use free_regions::RegionRelations;
@@ -23,7 +24,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::undo_log::Rollback;
 use rustc_data_structures::unify as ut;
-use rustc_errors::{Diag, DiagCtxt, ErrorGuaranteed};
+use rustc_errors::{Diag, ErrorGuaranteed};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_macros::extension;
 use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
@@ -44,7 +45,7 @@ use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
 use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
 use rustc_middle::{bug, span_bug};
 use rustc_span::symbol::Symbol;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use snapshot::undo_log::InferCtxtUndoLogs;
 use std::cell::{Cell, RefCell};
 use std::fmt;
@@ -334,149 +335,6 @@ pub struct InferCtxt<'tcx> {
     pub obligation_inspector: Cell<Option<ObligationInspector<'tcx>>>,
 }
 
-impl<'tcx> ty::InferCtxtLike for InferCtxt<'tcx> {
-    type Interner = TyCtxt<'tcx>;
-
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.tcx
-    }
-
-    fn universe_of_ty(&self, vid: 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.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: ConstVid) -> Option<ty::UniverseIndex> {
-        // Same issue as with `universe_of_ty`
-        match self.probe_const_var(ct) {
-            Err(universe) => Some(universe),
-            Ok(_) => None,
-        }
-    }
-
-    fn root_ty_var(&self, var: TyVid) -> TyVid {
-        self.root_var(var)
-    }
-
-    fn root_const_var(&self, var: ConstVid) -> ConstVid {
-        self.root_const_var(var)
-    }
-
-    fn opportunistic_resolve_ty_var(&self, vid: 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: IntVid) -> Ty<'tcx> {
-        self.opportunistic_resolve_int_var(vid)
-    }
-
-    fn opportunistic_resolve_float_var(&self, vid: FloatVid) -> Ty<'tcx> {
-        self.opportunistic_resolve_float_var(vid)
-    }
-
-    fn opportunistic_resolve_ct_var(&self, vid: 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: EffectVid) -> ty::Const<'tcx> {
-        match self.probe_effect_var(vid) {
-            Some(ct) => ct,
-            None => {
-                ty::Const::new_infer(self.tcx, 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<Self::Interner> + 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 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())
-    }
-}
-
 /// See the `error_reporting` module for more details.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)]
 pub enum ValuePairs<'tcx> {
@@ -826,10 +684,14 @@ impl<'tcx> InferOk<'tcx, ()> {
 }
 
 impl<'tcx> InferCtxt<'tcx> {
-    pub fn dcx(&self) -> &'tcx DiagCtxt {
+    pub fn dcx(&self) -> DiagCtxtHandle<'tcx> {
         self.tcx.dcx()
     }
 
+    pub fn defining_opaque_types(&self) -> &'tcx ty::List<LocalDefId> {
+        self.defining_opaque_types
+    }
+
     pub fn next_trait_solver(&self) -> bool {
         self.next_trait_solver
     }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index c95a10f4e8d..41c8b941717 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::jobserver;
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::registry::Registry;
-use rustc_errors::{DiagCtxt, ErrorGuaranteed};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed};
 use rustc_lint::LintStore;
 use rustc_middle::ty;
 use rustc_middle::ty::CurrentGcx;
@@ -46,7 +46,7 @@ pub struct Compiler {
 }
 
 /// Converts strings provided as `--cfg [cfgspec]` into a `Cfg`.
-pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg {
+pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec<String>) -> Cfg {
     cfgs.into_iter()
         .map(|s| {
             let psess = ParseSess::with_silent_emitter(
@@ -105,7 +105,7 @@ pub(crate) fn parse_cfg(dcx: &DiagCtxt, cfgs: Vec<String>) -> Cfg {
 }
 
 /// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
-pub(crate) fn parse_check_cfg(dcx: &DiagCtxt, specs: Vec<String>) -> CheckCfg {
+pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec<String>) -> CheckCfg {
     // If any --check-cfg is passed then exhaustive_values and exhaustive_names
     // are enabled by default.
     let exhaustive_names = !specs.is_empty();
@@ -451,12 +451,12 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 
             codegen_backend.init(&sess);
 
-            let cfg = parse_cfg(&sess.dcx(), config.crate_cfg);
+            let cfg = parse_cfg(sess.dcx(), config.crate_cfg);
             let mut cfg = config::build_configuration(&sess, cfg);
             util::add_configuration(&mut cfg, &mut sess, &*codegen_backend);
             sess.psess.config = cfg;
 
-            let mut check_cfg = parse_check_cfg(&sess.dcx(), config.crate_check_cfg);
+            let mut check_cfg = parse_check_cfg(sess.dcx(), config.crate_check_cfg);
             check_cfg.fill_well_known(&sess.target);
             sess.psess.check_config = check_cfg;
 
@@ -529,7 +529,7 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
 }
 
 pub fn try_print_query_stack(
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     num_frames: Option<usize>,
     file: Option<std::fs::File>,
 ) {
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index 6538995926a..2909f8adfb0 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -70,7 +70,7 @@ where
             Arc::default(),
             Default::default(),
         );
-        let cfg = parse_cfg(&sess.dcx(), matches.opt_strs("cfg"));
+        let cfg = parse_cfg(sess.dcx(), matches.opt_strs("cfg"));
         let cfg = build_configuration(&sess, cfg);
         f(sess, cfg)
     });
@@ -761,7 +761,7 @@ fn test_unstable_options_tracking_hash() {
         })
     );
     tracked!(codegen_backend, Some("abc".to_string()));
-    tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc });
+    tracked!(coverage_options, CoverageOptions { level: CoverageLevel::Mcdc, no_mir_spans: true });
     tracked!(crate_attr, vec!["abc".to_string()]);
     tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
     tracked!(debug_info_for_profiling, true);
diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
index a311c274a6b..d4f6d388d9f 100644
--- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs
+++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs
@@ -11,7 +11,7 @@ use rustc_middle::ty::{
     self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
 };
 use rustc_session::{declare_lint, declare_lint_pass};
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::{sym, Span};
 
 use crate::fluent_generated as fluent;
 use crate::{LateContext, LateLintPass};
@@ -53,7 +53,7 @@ declare_lint! {
     /// while the `impl Display` is live.
     ///
     /// To fix this, we can explicitly state that the `impl Display` doesn't
-    /// capture any lifetimes, using `impl use<> Display`.
+    /// capture any lifetimes, using `impl Display + use<>`.
     pub IMPL_TRAIT_OVERCAPTURES,
     Allow,
     "`impl Trait` will capture more lifetimes than possibly intended in edition 2024",
@@ -79,7 +79,7 @@ declare_lint! {
     /// # #![feature(precise_capturing, lifetime_capture_rules_2024)]
     /// # #![allow(incomplete_features)]
     /// # #![deny(impl_trait_redundant_captures)]
-    /// fn test<'a>(x: &'a i32) -> impl use<'a> Sized { x }
+    /// fn test<'a>(x: &'a i32) -> impl Sized + use<'a> { x }
     /// ```
     ///
     /// {{produces}}
@@ -249,7 +249,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
             // If we have uncaptured args, and if the opaque doesn't already have
             // `use<>` syntax on it, and we're < edition 2024, then warn the user.
             if !new_capture_rules
-                && opaque.precise_capturing_args.is_none()
+                && !opaque.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Use(..)))
                 && !uncaptured_spans.is_empty()
             {
                 let suggestion = if let Ok(snippet) =
@@ -268,8 +268,8 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
                     // Make sure that we're not trying to name any APITs
                     if generics.iter().all(|name| !name.starts_with("impl ")) {
                         Some((
-                            format!(" use<{}>", generics.join(", ")),
-                            opaque_span.with_lo(opaque_span.lo() + BytePos(4)).shrink_to_lo(),
+                            format!(" + use<{}>", generics.join(", ")),
+                            opaque_span.shrink_to_hi(),
                         ))
                     } else {
                         None
@@ -294,7 +294,11 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for VisitOpaqueTypes<'tcx> {
             // have no uncaptured args, then we should warn to the user that
             // it's redundant to capture all args explicitly.
             else if new_capture_rules
-                && let Some((captured_args, capturing_span)) = opaque.precise_capturing_args
+                && let Some((captured_args, capturing_span)) =
+                    opaque.bounds.iter().find_map(|bound| match *bound {
+                        hir::GenericBound::Use(a, s) => Some((a, s)),
+                        _ => None,
+                    })
             {
                 let mut explicitly_captured = UnordSet::default();
                 for arg in captured_args {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index f60f8f7c6b7..4ad31ccc280 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -1405,7 +1405,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                     diag.note(fluent::lint_macro_to_change);
                 }
                 if let Some(cargo_update) = cargo_update {
-                    diag.subdiagnostic(&diag.dcx, cargo_update);
+                    diag.subdiagnostic(cargo_update);
                 }
 
                 if has_trait {
@@ -1471,7 +1471,7 @@ impl<'a> LintDiagnostic<'a, ()> for NonLocalDefinitionsDiag {
                 diag.note(fluent::lint_non_local_definitions_deprecation);
 
                 if let Some(cargo_update) = cargo_update {
-                    diag.subdiagnostic(&diag.dcx, cargo_update);
+                    diag.subdiagnostic(cargo_update);
                 }
             }
         }
@@ -1957,7 +1957,7 @@ impl<'a> LintDiagnostic<'a, ()> for UnusedDef<'_, '_> {
             diag.note(note.to_string());
         }
         if let Some(sugg) = self.suggestion {
-            diag.subdiagnostic(diag.dcx, sugg);
+            diag.subdiagnostic(sugg);
         }
     }
 }
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 7a1aa4043be..195a0f72475 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -1268,7 +1268,7 @@ impl EarlyLintPass for UnusedParens {
                     ast::TyKind::TraitObject(..) => {}
                     ast::TyKind::BareFn(b)
                         if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
-                    ast::TyKind::ImplTrait(_, bounds, _) if bounds.len() > 1 => {}
+                    ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
                     _ => {
                         let spans = if !ty.span.from_expansion() {
                             r.span
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
index ef6005283d6..a3abbdcf18c 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs
@@ -78,7 +78,7 @@ impl<'a> DiagnosticDerive<'a> {
                 #[track_caller]
                 fn into_diag(
                     self,
-                    dcx: &'_sess rustc_errors::DiagCtxt,
+                    dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                     level: rustc_errors::Level
                 ) -> rustc_errors::Diag<'_sess, G> {
                     #implementation
diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
index 38d4a5ee61c..46bd80c2df6 100644
--- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
+++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs
@@ -335,7 +335,7 @@ impl DiagnosticDeriveVariantBuilder {
                 }
             }
             (Meta::Path(_), "subdiagnostic") => {
-                return Ok(quote! { diag.subdiagnostic(diag.dcx, #binding); });
+                return Ok(quote! { diag.subdiagnostic(#binding); });
             }
             _ => (),
         }
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index ad283117d7e..749495bc2ef 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::owned_slice::OwnedSlice;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::{self, FreezeReadGuard, FreezeWriteGuard};
-use rustc_errors::DiagCtxt;
+use rustc_errors::DiagCtxtHandle;
 use rustc_expand::base::SyntaxExtension;
 use rustc_fs_util::try_canonicalize;
 use rustc_hir::def_id::{CrateNum, LocalDefId, StableCrateId, LOCAL_CRATE};
@@ -91,8 +91,8 @@ impl<'a, 'tcx> std::ops::Deref for CrateLoader<'a, 'tcx> {
 }
 
 impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
-    fn dcx(&self) -> &'tcx DiagCtxt {
-        &self.tcx.dcx()
+    fn dcx(&self) -> DiagCtxtHandle<'tcx> {
+        self.tcx.dcx()
     }
 }
 
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index 47d183a0440..b0d82a0e3b7 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -3,7 +3,7 @@ use std::{
     path::{Path, PathBuf},
 };
 
-use rustc_errors::{codes::*, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::{codes::*, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{sym, Span, Symbol};
 use rustc_target::spec::{PanicStrategy, TargetTriple};
@@ -503,7 +503,7 @@ pub(crate) struct MultipleCandidates {
 }
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for MultipleCandidates {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::metadata_multiple_candidates);
         diag.arg("crate_name", self.crate_name);
         diag.arg("flavor", self.flavor);
@@ -602,7 +602,7 @@ pub struct InvalidMetadataFiles {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for InvalidMetadataFiles {
     #[track_caller]
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::metadata_invalid_meta_files);
         diag.arg("crate_name", self.crate_name);
         diag.arg("add_info", self.add_info);
@@ -631,7 +631,7 @@ pub struct CannotFindCrate {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for CannotFindCrate {
     #[track_caller]
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::metadata_cannot_find_crate);
         diag.arg("crate_name", self.crate_name);
         diag.arg("current_crate", self.current_crate);
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index 3d4e5caa9b2..e3d7dff3c66 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -111,7 +111,7 @@ macro_rules! arena_types {
                     rustc_middle::ty::EarlyBinder<'tcx, rustc_middle::ty::Ty<'tcx>>
                 >,
             [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<rustc_middle::ty::TyCtxt<'tcx>>,
-            [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<'tcx>,
+            [] predefined_opaques_in_body: rustc_middle::traits::solve::PredefinedOpaquesData<rustc_middle::ty::TyCtxt<'tcx>>,
             [decode] doc_link_resolutions: rustc_hir::def::DocLinkResMap,
             [] stripped_cfg_items: rustc_ast::expand::StrippedCfgItem,
             [] mod_child: rustc_middle::metadata::ModChild,
diff --git a/compiler/rustc_middle/src/macros.rs b/compiler/rustc_middle/src/macros.rs
index 817ac594627..fcea1ea81a7 100644
--- a/compiler/rustc_middle/src/macros.rs
+++ b/compiler/rustc_middle/src/macros.rs
@@ -4,10 +4,10 @@
 ///
 /// If you have a span available, you should use [`span_bug`] instead.
 ///
-/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxt::span_delayed_bug`]
+/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`]
 /// may be useful.
 ///
-/// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug
+/// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug
 /// [`span_bug`]: crate::span_bug
 #[macro_export]
 macro_rules! bug {
@@ -30,10 +30,10 @@ macro_rules! bug {
 /// at the code the compiler was compiling when it ICEd. This is the preferred way to trigger
 /// ICEs.
 ///
-/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxt::span_delayed_bug`]
+/// If the bug should only be emitted when compilation didn't fail, [`DiagCtxtHandle::span_delayed_bug`]
 /// may be useful.
 ///
-/// [`DiagCtxt::span_delayed_bug`]: rustc_errors::DiagCtxt::span_delayed_bug
+/// [`DiagCtxtHandle::span_delayed_bug`]: rustc_errors::DiagCtxtHandle::span_delayed_bug
 #[macro_export]
 macro_rules! span_bug {
     ($span:expr, $msg:expr) => (
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 8cf1bedf0da..d1ccd158cf9 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -176,7 +176,7 @@ impl<'a, G: EmissionGuarantee> rustc_errors::LintDiagnostic<'a, G> for Deprecate
             diag.arg("has_note", false);
         }
         if let Some(sub) = self.sub {
-            diag.subdiagnostic(diag.dcx, sub);
+            diag.subdiagnostic(sub);
         }
     }
 }
diff --git a/compiler/rustc_middle/src/mir/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs
index b0f8a047b82..2fc466c0e7e 100644
--- a/compiler/rustc_middle/src/mir/interpret/allocation.rs
+++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs
@@ -52,7 +52,7 @@ impl AllocBytes for Box<[u8]> {
     }
 
     fn zeroed(size: Size, _align: Align) -> Option<Self> {
-        let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).ok()?;
+        let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes().try_into().ok()?).ok()?;
         // SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]>
         let bytes = unsafe { bytes.assume_init() };
         Some(bytes)
@@ -323,7 +323,10 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
     /// first call this function and then call write_scalar to fill in the right data.
     pub fn uninit(size: Size, align: Align) -> Self {
         match Self::uninit_inner(size, align, || {
-            panic!("Allocation::uninit called with panic_on_fail had allocation failure");
+            panic!(
+                "interpreter ran out of memory: cannot create allocation of {} bytes",
+                size.bytes()
+            );
         }) {
             Ok(x) => x,
             Err(x) => x,
diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs
index 9e979620a44..90f80f90767 100644
--- a/compiler/rustc_middle/src/traits/solve.rs
+++ b/compiler/rustc_middle/src/traits/solve.rs
@@ -5,12 +5,12 @@ use rustc_type_ir as ir;
 pub use rustc_type_ir::solve::*;
 
 use crate::ty::{
-    self, FallibleTypeFolder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
+    self, FallibleTypeFolder, TyCtxt, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor,
 };
 
 mod cache;
 
-pub use cache::{CacheData, EvaluationCache};
+pub use cache::EvaluationCache;
 
 pub type Goal<'tcx, P> = ir::solve::Goal<TyCtxt<'tcx>, P>;
 pub type QueryInput<'tcx, P> = ir::solve::QueryInput<TyCtxt<'tcx>, P>;
@@ -19,17 +19,11 @@ pub type CandidateSource<'tcx> = ir::solve::CandidateSource<TyCtxt<'tcx>>;
 pub type CanonicalInput<'tcx, P = ty::Predicate<'tcx>> = ir::solve::CanonicalInput<TyCtxt<'tcx>, P>;
 pub type CanonicalResponse<'tcx> = ir::solve::CanonicalResponse<TyCtxt<'tcx>>;
 
-/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)]
-pub struct PredefinedOpaquesData<'tcx> {
-    pub opaque_types: Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>,
-}
-
 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, HashStable)]
-pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<'tcx>>);
+pub struct PredefinedOpaques<'tcx>(pub(crate) Interned<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>);
 
 impl<'tcx> std::ops::Deref for PredefinedOpaques<'tcx> {
-    type Target = PredefinedOpaquesData<'tcx>;
+    type Target = PredefinedOpaquesData<TyCtxt<'tcx>>;
 
     fn deref(&self) -> &Self::Target {
         &self.0
diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs
index dc31114b2c4..72a8d4eb405 100644
--- a/compiler/rustc_middle/src/traits/solve/cache.rs
+++ b/compiler/rustc_middle/src/traits/solve/cache.rs
@@ -5,6 +5,8 @@ use rustc_data_structures::sync::Lock;
 use rustc_query_system::cache::WithDepNode;
 use rustc_query_system::dep_graph::DepNodeIndex;
 use rustc_session::Limit;
+use rustc_type_ir::solve::CacheData;
+
 /// The trait solver cache used by `-Znext-solver`.
 ///
 /// FIXME(@lcnr): link to some official documentation of how
@@ -14,17 +16,9 @@ pub struct EvaluationCache<'tcx> {
     map: Lock<FxHashMap<CanonicalInput<'tcx>, CacheEntry<'tcx>>>,
 }
 
-#[derive(Debug, PartialEq, Eq)]
-pub struct CacheData<'tcx> {
-    pub result: QueryResult<'tcx>,
-    pub proof_tree: Option<&'tcx inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>>>,
-    pub additional_depth: usize,
-    pub encountered_overflow: bool,
-}
-
-impl<'tcx> EvaluationCache<'tcx> {
+impl<'tcx> rustc_type_ir::inherent::EvaluationCache<TyCtxt<'tcx>> for &'tcx EvaluationCache<'tcx> {
     /// Insert a final result into the global cache.
-    pub fn insert(
+    fn insert(
         &self,
         tcx: TyCtxt<'tcx>,
         key: CanonicalInput<'tcx>,
@@ -48,7 +42,7 @@ impl<'tcx> EvaluationCache<'tcx> {
         if cfg!(debug_assertions) {
             drop(map);
             let expected = CacheData { result, proof_tree, additional_depth, encountered_overflow };
-            let actual = self.get(tcx, key, [], Limit(additional_depth));
+            let actual = self.get(tcx, key, [], additional_depth);
             if !actual.as_ref().is_some_and(|actual| expected == *actual) {
                 bug!("failed to lookup inserted element for {key:?}: {expected:?} != {actual:?}");
             }
@@ -59,13 +53,13 @@ impl<'tcx> EvaluationCache<'tcx> {
     /// and handling root goals of coinductive cycles.
     ///
     /// If this returns `Some` the cache result can be used.
-    pub fn get(
+    fn get(
         &self,
         tcx: TyCtxt<'tcx>,
         key: CanonicalInput<'tcx>,
         stack_entries: impl IntoIterator<Item = CanonicalInput<'tcx>>,
-        available_depth: Limit,
-    ) -> Option<CacheData<'tcx>> {
+        available_depth: usize,
+    ) -> Option<CacheData<TyCtxt<'tcx>>> {
         let map = self.map.borrow();
         let entry = map.get(&key)?;
 
@@ -76,7 +70,7 @@ impl<'tcx> EvaluationCache<'tcx> {
         }
 
         if let Some(ref success) = entry.success {
-            if available_depth.value_within_limit(success.additional_depth) {
+            if Limit(available_depth).value_within_limit(success.additional_depth) {
                 let QueryData { result, proof_tree } = success.data.get(tcx);
                 return Some(CacheData {
                     result,
@@ -87,12 +81,12 @@ impl<'tcx> EvaluationCache<'tcx> {
             }
         }
 
-        entry.with_overflow.get(&available_depth.0).map(|e| {
+        entry.with_overflow.get(&available_depth).map(|e| {
             let QueryData { result, proof_tree } = e.get(tcx);
             CacheData {
                 result,
                 proof_tree,
-                additional_depth: available_depth.0,
+                additional_depth: available_depth,
                 encountered_overflow: true,
             }
         })
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 684b3233cfd..8e221cdc603 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -205,6 +205,14 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
         self.did()
     }
 
+    fn is_struct(self) -> bool {
+        self.is_struct()
+    }
+
+    fn struct_tail_ty(self, interner: TyCtxt<'tcx>) -> Option<ty::EarlyBinder<'tcx, Ty<'tcx>>> {
+        Some(interner.type_of(self.non_enum_variant().tail_opt()?.did))
+    }
+
     fn is_phantom_data(self) -> bool {
         self.is_phantom_data()
     }
@@ -212,7 +220,7 @@ impl<'tcx> rustc_type_ir::inherent::AdtDef<TyCtxt<'tcx>> for AdtDef<'tcx> {
     fn all_field_tys(
         self,
         tcx: TyCtxt<'tcx>,
-    ) -> ty::EarlyBinder<'tcx, impl Iterator<Item = Ty<'tcx>>> {
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = Ty<'tcx>>> {
         ty::EarlyBinder::bind(
             self.all_fields().map(move |field| tcx.type_of(field.did).skip_binder()),
         )
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 12f0c38b054..32d01d07c17 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -16,8 +16,8 @@ mod valtree;
 
 pub use int::*;
 pub use kind::*;
-use rustc_span::Span;
 use rustc_span::DUMMY_SP;
+use rustc_span::{ErrorGuaranteed, Span};
 pub use valtree::*;
 
 pub type ConstKind<'tcx> = ir::ConstKind<TyCtxt<'tcx>>;
@@ -176,6 +176,10 @@ impl<'tcx> rustc_type_ir::inherent::Const<TyCtxt<'tcx>> for Const<'tcx> {
     fn new_expr(interner: TyCtxt<'tcx>, expr: ty::Expr<'tcx>) -> Self {
         Const::new_expr(interner, expr)
     }
+
+    fn new_error(interner: TyCtxt<'tcx>, guar: ErrorGuaranteed) -> Self {
+        Const::new_error(interner, guar)
+    }
 }
 
 impl<'tcx> Const<'tcx> {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index e2f15dac019..6d64c1d50ae 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -47,7 +47,9 @@ use rustc_data_structures::sync::{self, FreezeReadGuard, Lock, Lrc, RwLock, Work
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sync::{DynSend, DynSync};
 use rustc_data_structures::unord::UnordSet;
-use rustc_errors::{Applicability, Diag, DiagCtxt, ErrorGuaranteed, LintDiagnostic, MultiSpan};
+use rustc_errors::{
+    Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, LintDiagnostic, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
@@ -71,6 +73,7 @@ use rustc_target::abi::{FieldIdx, Layout, LayoutS, TargetDataLayout, VariantIdx}
 use rustc_target::spec::abi;
 use rustc_type_ir::fold::TypeFoldable;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::solve::SolverMode;
 use rustc_type_ir::TyKind::*;
 use rustc_type_ir::{CollectAndApply, Interner, TypeFlags, WithCachedTypeInfo};
 use tracing::{debug, instrument};
@@ -89,46 +92,65 @@ use std::ops::{Bound, Deref};
 impl<'tcx> Interner for TyCtxt<'tcx> {
     type DefId = DefId;
     type LocalDefId = LocalDefId;
-    type AdtDef = ty::AdtDef<'tcx>;
-
     type GenericArgs = ty::GenericArgsRef<'tcx>;
+
     type GenericArgsSlice = &'tcx [ty::GenericArg<'tcx>];
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
-
     type BoundVarKinds = &'tcx List<ty::BoundVariableKind>;
-    type BoundVarKind = ty::BoundVariableKind;
 
-    type CanonicalVars = CanonicalVarInfos<'tcx>;
+    type BoundVarKind = ty::BoundVariableKind;
     type PredefinedOpaques = solve::PredefinedOpaques<'tcx>;
+
+    fn mk_predefined_opaques_in_body(
+        self,
+        data: PredefinedOpaquesData<Self>,
+    ) -> Self::PredefinedOpaques {
+        self.mk_predefined_opaques_in_body(data)
+    }
     type DefiningOpaqueTypes = &'tcx ty::List<LocalDefId>;
-    type ExternalConstraints = ExternalConstraints<'tcx>;
     type CanonicalGoalEvaluationStepRef =
         &'tcx solve::inspect::CanonicalGoalEvaluationStep<TyCtxt<'tcx>>;
+    type CanonicalVars = CanonicalVarInfos<'tcx>;
+    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
+        self.mk_canonical_var_infos(infos)
+    }
 
+    type ExternalConstraints = ExternalConstraints<'tcx>;
+    fn mk_external_constraints(
+        self,
+        data: ExternalConstraintsData<Self>,
+    ) -> ExternalConstraints<'tcx> {
+        self.mk_external_constraints(data)
+    }
+    type DepNodeIndex = DepNodeIndex;
+    fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, DepNodeIndex) {
+        self.dep_graph.with_anon_task(self, crate::dep_graph::dep_kinds::TraitSelect, task)
+    }
     type Ty = Ty<'tcx>;
     type Tys = &'tcx List<Ty<'tcx>>;
+
     type FnInputTys = &'tcx [Ty<'tcx>];
     type ParamTy = ParamTy;
     type BoundTy = ty::BoundTy;
-    type PlaceholderTy = ty::PlaceholderType;
 
+    type PlaceholderTy = ty::PlaceholderType;
     type ErrorGuaranteed = ErrorGuaranteed;
     type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
-    type AllocId = crate::mir::interpret::AllocId;
 
+    type AllocId = crate::mir::interpret::AllocId;
     type Pat = Pattern<'tcx>;
     type Safety = hir::Safety;
     type Abi = abi::Abi;
-
     type Const = ty::Const<'tcx>;
     type PlaceholderConst = ty::PlaceholderConst;
+
     type ParamConst = ty::ParamConst;
     type BoundConst = ty::BoundVar;
     type ValueConst = ty::ValTree<'tcx>;
     type ExprConst = ty::Expr<'tcx>;
-
     type Region = Region<'tcx>;
+
     type EarlyParamRegion = ty::EarlyParamRegion;
     type LateParamRegion = ty::LateParamRegion;
     type BoundRegion = ty::BoundRegion;
@@ -136,15 +158,21 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
 
     type ParamEnv = ty::ParamEnv<'tcx>;
     type Predicate = Predicate<'tcx>;
+
     type Clause = Clause<'tcx>;
     type Clauses = ty::Clauses<'tcx>;
 
-    fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
-        self.expand_abstract_consts(t)
+    type EvaluationCache = &'tcx solve::EvaluationCache<'tcx>;
+
+    fn evaluation_cache(self, mode: SolverMode) -> &'tcx solve::EvaluationCache<'tcx> {
+        match mode {
+            SolverMode::Normal => &self.new_solver_evaluation_cache,
+            SolverMode::Coherence => &self.new_solver_coherence_evaluation_cache,
+        }
     }
 
-    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
-        self.mk_canonical_var_infos(infos)
+    fn expand_abstract_consts<T: TypeFoldable<TyCtxt<'tcx>>>(self, t: T) -> T {
+        self.expand_abstract_consts(t)
     }
 
     type GenericsOf = &'tcx ty::Generics;
@@ -163,6 +191,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.type_of(def_id)
     }
 
+    type AdtDef = ty::AdtDef<'tcx>;
+    fn adt_def(self, adt_def_id: DefId) -> Self::AdtDef {
+        self.adt_def(adt_def_id)
+    }
+
     fn alias_ty_kind(self, alias: ty::AliasTy<'tcx>) -> ty::AliasTyKind {
         match self.def_kind(alias.def_id) {
             DefKind::AssocTy => {
@@ -200,8 +233,8 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn trait_ref_and_own_args_for_alias(
         self,
         def_id: DefId,
-        args: Self::GenericArgs,
-    ) -> (rustc_type_ir::TraitRef<Self>, Self::GenericArgsSlice) {
+        args: ty::GenericArgsRef<'tcx>,
+    ) -> (ty::TraitRef<'tcx>, &'tcx [ty::GenericArg<'tcx>]) {
         assert_matches!(self.def_kind(def_id), DefKind::AssocTy | DefKind::AssocConst);
         let trait_def_id = self.parent(def_id);
         assert_matches!(self.def_kind(trait_def_id), DefKind::Trait);
@@ -212,18 +245,22 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         )
     }
 
-    fn mk_args(self, args: &[Self::GenericArg]) -> Self::GenericArgs {
+    fn mk_args(self, args: &[Self::GenericArg]) -> ty::GenericArgsRef<'tcx> {
         self.mk_args(args)
     }
 
     fn mk_args_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
-        T: CollectAndApply<Self::GenericArg, Self::GenericArgs>,
+        T: CollectAndApply<Self::GenericArg, ty::GenericArgsRef<'tcx>>,
     {
         self.mk_args_from_iter(args)
     }
 
+    fn check_args_compatible(self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> bool {
+        self.check_args_compatible(def_id, args)
+    }
+
     fn check_and_mk_args(
         self,
         def_id: DefId,
@@ -242,7 +279,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     fn mk_type_list_from_iter<I, T>(self, args: I) -> T::Output
     where
         I: Iterator<Item = T>,
-        T: CollectAndApply<Self::Ty, Self::Tys>,
+        T: CollectAndApply<Ty<'tcx>, &'tcx List<Ty<'tcx>>>,
     {
         self.mk_type_list_from_iter(args)
     }
@@ -291,6 +328,24 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.item_bounds(def_id).map_bound(IntoIterator::into_iter)
     }
 
+    fn predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.predicates_of(def_id).instantiate_identity(self).predicates.into_iter(),
+        )
+    }
+
+    fn own_predicates_of(
+        self,
+        def_id: DefId,
+    ) -> ty::EarlyBinder<'tcx, impl IntoIterator<Item = ty::Clause<'tcx>>> {
+        ty::EarlyBinder::bind(
+            self.predicates_of(def_id).instantiate_own_identity().map(|(clause, _)| clause),
+        )
+    }
+
     fn super_predicates_of(
         self,
         def_id: DefId,
@@ -305,15 +360,11 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     }
 
     fn require_lang_item(self, lang_item: TraitSolverLangItem) -> DefId {
-        self.require_lang_item(
-            match lang_item {
-                TraitSolverLangItem::Future => hir::LangItem::Future,
-                TraitSolverLangItem::FutureOutput => hir::LangItem::FutureOutput,
-                TraitSolverLangItem::AsyncFnKindHelper => hir::LangItem::AsyncFnKindHelper,
-                TraitSolverLangItem::AsyncFnKindUpvars => hir::LangItem::AsyncFnKindUpvars,
-            },
-            None,
-        )
+        self.require_lang_item(trait_lang_item_to_lang_item(lang_item), None)
+    }
+
+    fn is_lang_item(self, def_id: DefId, lang_item: TraitSolverLangItem) -> bool {
+        self.is_lang_item(def_id, trait_lang_item_to_lang_item(lang_item))
     }
 
     fn associated_type_def_ids(self, def_id: DefId) -> impl IntoIterator<Item = DefId> {
@@ -322,6 +373,257 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
             .filter(|assoc_item| matches!(assoc_item.kind, ty::AssocKind::Type))
             .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.
+    fn for_each_relevant_impl(
+        self,
+        trait_def_id: DefId,
+        self_ty: Ty<'tcx>,
+        mut f: impl FnMut(DefId),
+    ) {
+        let tcx = self;
+        let trait_impls = tcx.trait_impls_of(trait_def_id);
+        let mut consider_impls_for_simplified_type = |simp| {
+            if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
+                for &impl_def_id in impls_for_type {
+                    f(impl_def_id);
+                }
+            }
+        };
+
+        match self_ty.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(..)
+            | ty::CoroutineClosure(..)
+            | ty::Coroutine(_, _)
+            | ty::Never
+            | ty::Tuple(_) => {
+                let simp = ty::fast_reject::simplify_type(
+                    tcx,
+                    self_ty,
+                    ty::fast_reject::TreatParams::ForLookup,
+                )
+                .unwrap();
+                consider_impls_for_simplified_type(simp);
+            }
+
+            // HACK: For integer and float variables we have to manually look at all impls
+            // which have some integer or float as a self type.
+            ty::Infer(ty::IntVar(_)) => {
+                use ty::IntTy::*;
+                use ty::UintTy::*;
+                // This causes a compiler error if any new integer kinds are added.
+                let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy;
+                let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy;
+                let possible_integers = [
+                    // signed integers
+                    ty::SimplifiedType::Int(I8),
+                    ty::SimplifiedType::Int(I16),
+                    ty::SimplifiedType::Int(I32),
+                    ty::SimplifiedType::Int(I64),
+                    ty::SimplifiedType::Int(I128),
+                    ty::SimplifiedType::Int(Isize),
+                    // unsigned integers
+                    ty::SimplifiedType::Uint(U8),
+                    ty::SimplifiedType::Uint(U16),
+                    ty::SimplifiedType::Uint(U32),
+                    ty::SimplifiedType::Uint(U64),
+                    ty::SimplifiedType::Uint(U128),
+                    ty::SimplifiedType::Uint(Usize),
+                ];
+                for simp in possible_integers {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            ty::Infer(ty::FloatVar(_)) => {
+                // This causes a compiler error if any new float kinds are added.
+                let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128);
+                let possible_floats = [
+                    ty::SimplifiedType::Float(ty::FloatTy::F16),
+                    ty::SimplifiedType::Float(ty::FloatTy::F32),
+                    ty::SimplifiedType::Float(ty::FloatTy::F64),
+                    ty::SimplifiedType::Float(ty::FloatTy::F128),
+                ];
+
+                for simp in possible_floats {
+                    consider_impls_for_simplified_type(simp);
+                }
+            }
+
+            // The only traits applying to aliases and placeholders are blanket impls.
+            //
+            // Impls which apply to an alias after normalization are handled by
+            // `assemble_candidates_after_normalizing_self_ty`.
+            ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
+
+            // FIXME: These should ideally not exist as a self type. It would be nice for
+            // the builtin auto trait impls of coroutines to instead directly recurse
+            // into the witness.
+            ty::CoroutineWitness(..) => (),
+
+            // These variants should not exist as a self type.
+            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
+            | ty::Param(_)
+            | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
+        }
+
+        let trait_impls = tcx.trait_impls_of(trait_def_id);
+        for &impl_def_id in trait_impls.blanket_impls() {
+            f(impl_def_id);
+        }
+    }
+
+    fn has_item_definition(self, def_id: DefId) -> bool {
+        self.defaultness(def_id).has_value()
+    }
+
+    fn impl_is_default(self, impl_def_id: DefId) -> bool {
+        self.defaultness(impl_def_id).is_default()
+    }
+
+    fn impl_trait_ref(self, impl_def_id: DefId) -> ty::EarlyBinder<'tcx, ty::TraitRef<'tcx>> {
+        self.impl_trait_ref(impl_def_id).unwrap()
+    }
+
+    fn impl_polarity(self, impl_def_id: DefId) -> ty::ImplPolarity {
+        self.impl_polarity(impl_def_id)
+    }
+
+    fn trait_is_auto(self, trait_def_id: DefId) -> bool {
+        self.trait_is_auto(trait_def_id)
+    }
+
+    fn trait_is_alias(self, trait_def_id: DefId) -> bool {
+        self.trait_is_alias(trait_def_id)
+    }
+
+    fn trait_is_object_safe(self, trait_def_id: DefId) -> bool {
+        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 supertrait_def_ids(self, trait_def_id: DefId) -> impl IntoIterator<Item = DefId> {
+        self.supertrait_def_ids(trait_def_id)
+    }
+
+    fn delay_bug(self, msg: impl ToString) -> ErrorGuaranteed {
+        self.dcx().span_delayed_bug(DUMMY_SP, msg.to_string())
+    }
+
+    fn is_general_coroutine(self, coroutine_def_id: DefId) -> bool {
+        self.is_general_coroutine(coroutine_def_id)
+    }
+
+    fn coroutine_is_async(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_async(coroutine_def_id)
+    }
+
+    fn coroutine_is_gen(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_gen(coroutine_def_id)
+    }
+
+    fn coroutine_is_async_gen(self, coroutine_def_id: DefId) -> bool {
+        self.coroutine_is_async_gen(coroutine_def_id)
+    }
+
+    fn layout_is_pointer_like(self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+        self.layout_of(self.erase_regions(param_env.and(ty)))
+            .is_ok_and(|layout| layout.layout.is_pointer_like(&self.data_layout))
+    }
+
+    type UnsizingParams = &'tcx rustc_index::bit_set::BitSet<u32>;
+    fn unsizing_params_for_adt(self, adt_def_id: DefId) -> Self::UnsizingParams {
+        self.unsizing_params_for_adt(adt_def_id)
+    }
+
+    fn find_const_ty_from_env(
+        self,
+        param_env: ty::ParamEnv<'tcx>,
+        placeholder: Self::PlaceholderConst,
+    ) -> Ty<'tcx> {
+        placeholder.find_const_ty_from_env(param_env)
+    }
+}
+
+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,
+    }
+}
+
+impl<'tcx> rustc_type_ir::inherent::DefId<TyCtxt<'tcx>> for DefId {
+    fn as_local(self) -> Option<LocalDefId> {
+        self.as_local()
+    }
 }
 
 impl<'tcx> rustc_type_ir::inherent::Abi<TyCtxt<'tcx>> for abi::Abi {
@@ -356,6 +658,10 @@ impl<'tcx> rustc_type_ir::inherent::Features<TyCtxt<'tcx>> for &'tcx rustc_featu
     fn coroutine_clone(self) -> bool {
         self.coroutine_clone
     }
+
+    fn associated_const_equality(self) -> bool {
+        self.associated_const_equality
+    }
 }
 
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
@@ -384,7 +690,7 @@ pub struct CtxtInterners<'tcx> {
     layout: InternedSet<'tcx, LayoutS<FieldIdx, VariantIdx>>,
     adt_def: InternedSet<'tcx, AdtDefData>,
     external_constraints: InternedSet<'tcx, ExternalConstraintsData<TyCtxt<'tcx>>>,
-    predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<'tcx>>,
+    predefined_opaques_in_body: InternedSet<'tcx, PredefinedOpaquesData<TyCtxt<'tcx>>>,
     fields: InternedSet<'tcx, List<FieldIdx>>,
     local_def_ids: InternedSet<'tcx, List<LocalDefId>>,
     captures: InternedSet<'tcx, List<&'tcx ty::CapturedPlace<'tcx>>>,
@@ -1415,7 +1721,7 @@ impl<'tcx> TyCtxt<'tcx> {
         )
     }
 
-    pub fn dcx(self) -> &'tcx DiagCtxt {
+    pub fn dcx(self) -> DiagCtxtHandle<'tcx> {
         self.sess.dcx()
     }
 }
@@ -2096,7 +2402,7 @@ direct_interners! {
     adt_def: pub mk_adt_def_from_data(AdtDefData): AdtDef -> AdtDef<'tcx>,
     external_constraints: pub mk_external_constraints(ExternalConstraintsData<TyCtxt<'tcx>>):
         ExternalConstraints -> ExternalConstraints<'tcx>,
-    predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<'tcx>):
+    predefined_opaques_in_body: pub mk_predefined_opaques_in_body(PredefinedOpaquesData<TyCtxt<'tcx>>):
         PredefinedOpaques -> PredefinedOpaques<'tcx>,
 }
 
diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs
index 54c88e48614..83d45ca78d9 100644
--- a/compiler/rustc_middle/src/ty/generic_args.rs
+++ b/compiler/rustc_middle/src/ty/generic_args.rs
@@ -44,10 +44,27 @@ pub struct GenericArg<'tcx> {
 impl<'tcx> rustc_type_ir::inherent::GenericArg<TyCtxt<'tcx>> for GenericArg<'tcx> {}
 
 impl<'tcx> rustc_type_ir::inherent::GenericArgs<TyCtxt<'tcx>> for ty::GenericArgsRef<'tcx> {
+    fn rebase_onto(
+        self,
+        tcx: TyCtxt<'tcx>,
+        source_ancestor: DefId,
+        target_args: GenericArgsRef<'tcx>,
+    ) -> GenericArgsRef<'tcx> {
+        self.rebase_onto(tcx, source_ancestor, target_args)
+    }
+
     fn type_at(self, i: usize) -> Ty<'tcx> {
         self.type_at(i)
     }
 
+    fn region_at(self, i: usize) -> ty::Region<'tcx> {
+        self.region_at(i)
+    }
+
+    fn const_at(self, i: usize) -> ty::Const<'tcx> {
+        self.const_at(i)
+    }
+
     fn identity_for_item(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::GenericArgsRef<'tcx> {
         GenericArgs::identity_for_item(tcx, def_id)
     }
@@ -281,6 +298,7 @@ impl<'tcx> GenericArg<'tcx> {
     pub fn is_non_region_infer(self) -> bool {
         match self.unpack() {
             GenericArgKind::Lifetime(_) => false,
+            // FIXME: This shouldn't return numerical/float.
             GenericArgKind::Type(ty) => ty.is_ty_or_numeric_infer(),
             GenericArgKind::Const(ct) => ct.is_ct_infer(),
         }
diff --git a/compiler/rustc_middle/src/ty/generics.rs b/compiler/rustc_middle/src/ty/generics.rs
index 185dbe44735..6467689a8aa 100644
--- a/compiler/rustc_middle/src/ty/generics.rs
+++ b/compiler/rustc_middle/src/ty/generics.rs
@@ -392,6 +392,10 @@ impl<'tcx> GenericPredicates<'tcx> {
         EarlyBinder::bind(self.predicates).iter_instantiated_copied(tcx, args)
     }
 
+    pub fn instantiate_own_identity(&self) -> impl Iterator<Item = (Clause<'tcx>, Span)> {
+        EarlyBinder::bind(self.predicates).instantiate_identity_iter_copied()
+    }
+
     #[instrument(level = "debug", skip(self, tcx))]
     fn instantiate_into(
         &self,
diff --git a/compiler/rustc_middle/src/ty/layout.rs b/compiler/rustc_middle/src/ty/layout.rs
index cf7610bb4f6..3d397b6b37e 100644
--- a/compiler/rustc_middle/src/ty/layout.rs
+++ b/compiler/rustc_middle/src/ty/layout.rs
@@ -5,7 +5,7 @@ use crate::ty::normalize_erasing_regions::NormalizationError;
 use crate::ty::{self, CoroutineArgsExt, Ty, TyCtxt, TypeVisitableExt};
 use rustc_error_messages::DiagMessage;
 use rustc_errors::{
-    Diag, DiagArgValue, DiagCtxt, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
+    Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, IntoDiagArg, Level,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -1256,7 +1256,7 @@ pub enum FnAbiError<'tcx> {
 }
 
 impl<'a, 'b, G: EmissionGuarantee> Diagnostic<'a, G> for FnAbiError<'b> {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         match self {
             Self::Layout(e) => e.into_diagnostic().into_diag(dcx, level),
             Self::AdjustForForeignAbi(call::AdjustForForeignAbiError::Unsupported {
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index d07541bad93..9c2bfc12a18 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -990,6 +990,16 @@ pub struct ParamEnv<'tcx> {
     packed: CopyTaggedPtr<Clauses<'tcx>, ParamTag, true>,
 }
 
+impl<'tcx> rustc_type_ir::inherent::ParamEnv<TyCtxt<'tcx>> for ParamEnv<'tcx> {
+    fn reveal(self) -> Reveal {
+        self.reveal()
+    }
+
+    fn caller_bounds(self) -> impl IntoIterator<Item = ty::Clause<'tcx>> {
+        self.caller_bounds()
+    }
+}
+
 #[derive(Copy, Clone)]
 struct ParamTag {
     reveal: traits::Reveal,
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index ae36f2624ca..e9b37503bb3 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -175,6 +175,14 @@ pub struct Clause<'tcx>(
 
 impl<'tcx> rustc_type_ir::inherent::Clause<TyCtxt<'tcx>> for Clause<'tcx> {}
 
+impl<'tcx> rustc_type_ir::inherent::IntoKind for Clause<'tcx> {
+    type Kind = ty::Binder<'tcx, ClauseKind<'tcx>>;
+
+    fn kind(self) -> Self::Kind {
+        self.kind()
+    }
+}
+
 impl<'tcx> Clause<'tcx> {
     pub fn as_predicate(self) -> Predicate<'tcx> {
         Predicate(self.0)
@@ -251,6 +259,28 @@ impl<'tcx> ExistentialPredicate<'tcx> {
 
 pub type PolyExistentialPredicate<'tcx> = ty::Binder<'tcx, ExistentialPredicate<'tcx>>;
 
+impl<'tcx> rustc_type_ir::inherent::BoundExistentialPredicates<TyCtxt<'tcx>>
+    for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>
+{
+    fn principal_def_id(self) -> Option<DefId> {
+        self.principal_def_id()
+    }
+
+    fn principal(self) -> Option<ty::PolyExistentialTraitRef<'tcx>> {
+        self.principal()
+    }
+
+    fn auto_traits(self) -> impl IntoIterator<Item = DefId> {
+        self.auto_traits()
+    }
+
+    fn projection_bounds(
+        self,
+    ) -> impl IntoIterator<Item = ty::Binder<'tcx, ExistentialProjection<'tcx>>> {
+        self.projection_bounds()
+    }
+}
+
 impl<'tcx> ty::List<ty::PolyExistentialPredicate<'tcx>> {
     /// Returns the "principal `DefId`" of this set of existential predicates.
     ///
@@ -481,12 +511,6 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for Predicate<'tcx> {
     }
 }
 
-impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for TraitPredicate<'tcx> {
-    fn upcast_from(from: TraitRef<'tcx>, _tcx: TyCtxt<'tcx>) -> Self {
-        TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive }
-    }
-}
-
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, TraitRef<'tcx>> for Clause<'tcx> {
     fn upcast_from(from: TraitRef<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         let p: Predicate<'tcx> = from.upcast(tcx);
@@ -543,6 +567,12 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyTraitPredicate<'tcx>> for Clause<'tcx> {
     }
 }
 
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, RegionOutlivesPredicate<'tcx>> for Predicate<'tcx> {
+    fn upcast_from(from: RegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
+        ty::Binder::dummy(PredicateKind::Clause(ClauseKind::RegionOutlives(from))).upcast(tcx)
+    }
+}
+
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyRegionOutlivesPredicate<'tcx>> for Predicate<'tcx> {
     fn upcast_from(from: PolyRegionOutlivesPredicate<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         from.map_bound(|p| PredicateKind::Clause(ClauseKind::RegionOutlives(p))).upcast(tcx)
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 8308e537e5e..9c8a3484aa5 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -930,6 +930,22 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
     fn new_pat(interner: TyCtxt<'tcx>, ty: Self, pat: ty::Pattern<'tcx>) -> Self {
         Ty::new_pat(interner, ty, pat)
     }
+
+    fn new_unit(interner: TyCtxt<'tcx>) -> Self {
+        interner.types.unit
+    }
+
+    fn new_usize(interner: TyCtxt<'tcx>) -> Self {
+        interner.types.usize
+    }
+
+    fn discriminant_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
+        self.discriminant_ty(interner)
+    }
+
+    fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> {
+        self.async_destructor_ty(interner)
+    }
 }
 
 /// Type utilities
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 52a0e72e17e..b079ed521d3 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -565,42 +565,6 @@ impl<'tcx> TyCtxt<'tcx> {
         Ok(())
     }
 
-    /// Checks whether each generic argument is simply a unique generic placeholder.
-    ///
-    /// This is used in the new solver, which canonicalizes params to placeholders
-    /// for better caching.
-    pub fn uses_unique_placeholders_ignoring_regions(
-        self,
-        args: GenericArgsRef<'tcx>,
-    ) -> Result<(), NotUniqueParam<'tcx>> {
-        let mut seen = GrowableBitSet::default();
-        for arg in args {
-            match arg.unpack() {
-                // Ignore regions, since we can't resolve those in a canonicalized
-                // query in the trait solver.
-                GenericArgKind::Lifetime(_) => {}
-                GenericArgKind::Type(t) => match t.kind() {
-                    ty::Placeholder(p) => {
-                        if !seen.insert(p.bound.var) {
-                            return Err(NotUniqueParam::DuplicateParam(t.into()));
-                        }
-                    }
-                    _ => return Err(NotUniqueParam::NotParam(t.into())),
-                },
-                GenericArgKind::Const(c) => match c.kind() {
-                    ty::ConstKind::Placeholder(p) => {
-                        if !seen.insert(p.bound) {
-                            return Err(NotUniqueParam::DuplicateParam(c.into()));
-                        }
-                    }
-                    _ => return Err(NotUniqueParam::NotParam(c.into())),
-                },
-            }
-        }
-
-        Ok(())
-    }
-
     /// Returns `true` if `def_id` refers to a closure, coroutine, or coroutine-closure
     /// (i.e. an async closure). These are all represented by `hir::Closure`, and all
     /// have the same `DefKind`.
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 3bd2e47976b..7c73d8a6d47 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,9 +1,9 @@
 use crate::fluent_generated as fluent;
-use rustc_errors::DiagArgValue;
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level, MultiSpan,
+    codes::*, Applicability, Diag, Diagnostic, EmissionGuarantee, Level, MultiSpan,
     SubdiagMessageOp, Subdiagnostic,
 };
+use rustc_errors::{DiagArgValue, DiagCtxtHandle};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::{self, Ty};
 use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcPatCtxt};
@@ -492,7 +492,7 @@ pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
 }
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'_, G> {
         let mut diag =
             Diag::new(dcx, level, fluent::mir_build_non_exhaustive_patterns_type_not_empty);
         diag.span(self.scrut_span);
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 30f57c8c622..70065b5a2c3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -1137,7 +1137,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
 
     let all_arms_have_guards = arms.iter().all(|arm_id| thir[*arm_id].guard.is_some());
     if !is_empty_match && all_arms_have_guards {
-        err.subdiagnostic(cx.tcx.dcx(), NonExhaustiveMatchAllArmsGuarded);
+        err.subdiagnostic(NonExhaustiveMatchAllArmsGuarded);
     }
     if let Some((span, sugg)) = suggestion {
         err.span_suggestion_verbose(span, msg, sugg, Applicability::HasPlaceholders);
diff --git a/compiler/rustc_mir_transform/src/coverage/mappings.rs b/compiler/rustc_mir_transform/src/coverage/mappings.rs
index 0e209757100..759bb7c1f9d 100644
--- a/compiler/rustc_mir_transform/src/coverage/mappings.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mappings.rs
@@ -5,6 +5,7 @@ use rustc_index::bit_set::BitSet;
 use rustc_index::IndexVec;
 use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, ConditionInfo, 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};
@@ -63,31 +64,35 @@ pub(super) struct ExtractedMappings {
 
 /// Extracts coverage-relevant spans from MIR, and associates them with
 /// their corresponding BCBs.
-pub(super) fn extract_all_mapping_info_from_mir(
-    mir_body: &mir::Body<'_>,
+pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    mir_body: &mir::Body<'tcx>,
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
 ) -> ExtractedMappings {
-    if hir_info.is_async_fn {
+    let mut code_mappings = vec![];
+    let mut branch_pairs = vec![];
+    let mut mcdc_bitmap_bytes = 0;
+    let mut mcdc_branches = vec![];
+    let mut mcdc_decisions = vec![];
+
+    if hir_info.is_async_fn || tcx.sess.coverage_no_mir_spans() {
         // An async function desugars into a function that returns a future,
         // with the user code wrapped in a closure. Any spans in the desugared
         // outer function will be unhelpful, so just keep the signature span
         // and ignore all of the spans in the MIR body.
-        let mut mappings = ExtractedMappings::default();
+        //
+        // When debugging flag `-Zcoverage-options=no-mir-spans` is set, we need
+        // to give the same treatment to _all_ functions, because `llvm-cov`
+        // seems to ignore functions that don't have any ordinary code spans.
         if let Some(span) = hir_info.fn_sig_span_extended {
-            mappings.code_mappings.push(CodeMapping { span, bcb: START_BCB });
+            code_mappings.push(CodeMapping { span, bcb: START_BCB });
         }
-        return mappings;
+    } else {
+        // Extract coverage spans from MIR statements/terminators as normal.
+        extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
     }
 
-    let mut code_mappings = vec![];
-    let mut branch_pairs = vec![];
-    let mut mcdc_bitmap_bytes = 0;
-    let mut mcdc_branches = vec![];
-    let mut mcdc_decisions = vec![];
-
-    extract_refined_covspans(mir_body, hir_info, basic_coverage_blocks, &mut code_mappings);
-
     branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, basic_coverage_blocks));
 
     extract_mcdc_mappings(
diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs
index 419e39bc386..4a64d21f3d1 100644
--- a/compiler/rustc_mir_transform/src/coverage/mod.rs
+++ b/compiler/rustc_mir_transform/src/coverage/mod.rs
@@ -71,8 +71,12 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
 
     ////////////////////////////////////////////////////
     // Extract coverage spans and other mapping info from MIR.
-    let extracted_mappings =
-        mappings::extract_all_mapping_info_from_mir(mir_body, &hir_info, &basic_coverage_blocks);
+    let extracted_mappings = mappings::extract_all_mapping_info_from_mir(
+        tcx,
+        mir_body,
+        &hir_info,
+        &basic_coverage_blocks,
+    );
 
     ////////////////////////////////////////////////////
     // Create an optimized mix of `Counter`s and `Expression`s for the `CoverageGraph`. Ensure
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index b28dcb38cb6..dc7648d27b5 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -104,7 +104,7 @@ impl<'a> LintDiagnostic<'a, ()> for MustNotSupend<'_, '_> {
         diag.primary_message(fluent::mir_transform_must_not_suspend);
         diag.span_label(self.yield_sp, fluent::_subdiag::label);
         if let Some(reason) = self.reason {
-            diag.subdiagnostic(diag.dcx, reason);
+            diag.subdiagnostic(reason);
         }
         diag.span_help(self.src_sp, fluent::_subdiag::help);
         diag.arg("pre", self.pre);
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index 35fc78f2045..c0d1efd96c5 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -1,7 +1,7 @@
 use std::path::PathBuf;
 
 use crate::fluent_generated as fluent;
-use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_macros::{Diagnostic, LintDiagnostic};
 use rustc_span::{Span, Symbol};
 
@@ -48,7 +48,7 @@ pub struct UnusedGenericParamsHint {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for UnusedGenericParamsHint {
     #[track_caller]
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::monomorphize_unused_generic_params);
         diag.span(self.span);
         for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
diff --git a/compiler/rustc_next_trait_solver/Cargo.toml b/compiler/rustc_next_trait_solver/Cargo.toml
index 50dbc991f8f..3a5f438b432 100644
--- a/compiler/rustc_next_trait_solver/Cargo.toml
+++ b/compiler/rustc_next_trait_solver/Cargo.toml
@@ -5,9 +5,25 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
+bitflags = "2.4.1"
+derivative = "2.2.0"
+rustc_ast_ir = { path = "../rustc_ast_ir" }
+rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_index = { path = "../rustc_index", default-features = false }
+rustc_macros = { path = "../rustc_macros", optional = true }
+rustc_serialize = { path = "../rustc_serialize", optional = true }
 rustc_type_ir = { path = "../rustc_type_ir", default-features = false }
+rustc_type_ir_macros = { path = "../rustc_type_ir_macros" }
+tracing = "0.1"
 # tidy-alphabetical-end
 
 [features]
 default = ["nightly"]
-nightly = ["rustc_type_ir/nightly"]
+nightly = [
+    "rustc_ast_ir/nightly",
+    "rustc_data_structures",
+    "rustc_index/nightly",
+    "rustc_macros",
+    "rustc_serialize",
+    "rustc_type_ir/nightly",
+]
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index f22e24ef654..a81fd03d034 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -4,10 +4,11 @@ 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, InferCtxtLike,
-    Interner,
+    self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Interner,
 };
 
+use crate::infcx::SolverDelegate;
+
 /// Whether we're canonicalizing a query input or the query response.
 ///
 /// When canonicalizing an input we're in the context of the caller
@@ -37,7 +38,7 @@ pub enum CanonicalizeMode {
     },
 }
 
-pub struct Canonicalizer<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
+pub struct Canonicalizer<'a, Infcx: SolverDelegate<Interner = I>, I: Interner> {
     infcx: &'a Infcx,
     canonicalize_mode: CanonicalizeMode,
 
@@ -46,7 +47,7 @@ pub struct Canonicalizer<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
     binder_index: ty::DebruijnIndex,
 }
 
-impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infcx, I> {
+impl<'a, Infcx: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, Infcx, I> {
     pub fn canonicalize<T: TypeFoldable<I>>(
         infcx: &'a Infcx,
         canonicalize_mode: CanonicalizeMode,
@@ -210,7 +211,7 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
     }
 }
 
-impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
+impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I>
     for Canonicalizer<'_, Infcx, I>
 {
     fn interner(&self) -> I {
diff --git a/compiler/rustc_next_trait_solver/src/infcx.rs b/compiler/rustc_next_trait_solver/src/infcx.rs
new file mode 100644
index 00000000000..c249eb94cf6
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/infcx.rs
@@ -0,0 +1,205 @@
+use std::fmt::Debug;
+
+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};
+
+pub trait SolverDelegate: Sized {
+    type Interner: Interner;
+    fn interner(&self) -> Self::Interner;
+
+    type Span: Copy;
+
+    fn solver_mode(&self) -> SolverMode;
+
+    fn build_with_canonical<V>(
+        interner: Self::Interner,
+        solver_mode: SolverMode,
+        canonical: &ty::Canonical<Self::Interner, V>,
+    ) -> (Self, V, ty::CanonicalVarValues<Self::Interner>)
+    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(
+        interner: 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,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        arg: <Self::Interner as Interner>::GenericArg,
+    ) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>;
+
+    fn clone_opaque_types_for_query_response(
+        &self,
+    ) -> Vec<(ty::OpaqueTypeKey<Self::Interner>, <Self::Interner as Interner>::Ty)>;
+
+    fn make_deduplicated_outlives_constraints(
+        &self,
+    ) -> Vec<ty::OutlivesPredicate<Self::Interner, <Self::Interner as Interner>::GenericArg>>;
+
+    fn instantiate_canonical<V>(
+        &self,
+        canonical: ty::Canonical<Self::Interner, V>,
+        values: ty::CanonicalVarValues<Self::Interner>,
+    ) -> V
+    where
+        V: TypeFoldable<Self::Interner>;
+
+    fn instantiate_canonical_var_with_infer(
+        &self,
+        cv_info: ty::CanonicalVarInfo<Self::Interner>,
+        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
+    ) -> <Self::Interner as Interner>::GenericArg;
+
+    // FIXME: Can we implement this in terms of `add` and `inject`?
+    fn insert_hidden_type(
+        &self,
+        opaque_type_key: ty::OpaqueTypeKey<Self::Interner>,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+    ) -> Result<(), NoSolution>;
+
+    fn add_item_bounds_for_hidden_type(
+        &self,
+        def_id: <Self::Interner as Interner>::DefId,
+        args: <Self::Interner as Interner>::GenericArgs,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+        goals: &mut Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>,
+    );
+
+    fn inject_new_hidden_type_unchecked(
+        &self,
+        key: ty::OpaqueTypeKey<Self::Interner>,
+        hidden_ty: <Self::Interner as Interner>::Ty,
+    );
+
+    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,
+        goal_trait_ref: ty::TraitRef<Self::Interner>,
+        trait_assoc_def_id: <Self::Interner as Interner>::DefId,
+        impl_def_id: <Self::Interner as Interner>::DefId,
+    ) -> Result<Option<<Self::Interner as Interner>::DefId>, NoSolution>;
+
+    fn is_transmutable(
+        &self,
+        param_env: <Self::Interner as Interner>::ParamEnv,
+        dst: <Self::Interner as Interner>::Ty,
+        src: <Self::Interner as Interner>::Ty,
+        assume: <Self::Interner as Interner>::Const,
+    ) -> Result<Certainty, NoSolution>;
+}
diff --git a/compiler/rustc_next_trait_solver/src/lib.rs b/compiler/rustc_next_trait_solver/src/lib.rs
index 144caf36ee5..79c6925221e 100644
--- a/compiler/rustc_next_trait_solver/src/lib.rs
+++ b/compiler/rustc_next_trait_solver/src/lib.rs
@@ -4,6 +4,9 @@
 //! but were uplifted in the process of making the new trait solver generic.
 //! So if you got to this crate from the old solver, it's totally normal.
 
+#![feature(let_chains)]
+
 pub mod canonicalizer;
+pub mod infcx;
 pub mod resolve;
 pub mod solve;
diff --git a/compiler/rustc_next_trait_solver/src/resolve.rs b/compiler/rustc_next_trait_solver/src/resolve.rs
index 5c00b6978d6..3d8d957eaae 100644
--- a/compiler/rustc_next_trait_solver/src/resolve.rs
+++ b/compiler/rustc_next_trait_solver/src/resolve.rs
@@ -1,27 +1,28 @@
+use crate::infcx::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, InferCtxtLike, Interner};
+use rustc_type_ir::{self as ty, Interner};
 
 ///////////////////////////////////////////////////////////////////////////
 // EAGER RESOLUTION
 
 /// Resolves ty, region, and const vars to their inferred values or their root vars.
-pub struct EagerResolver<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+pub struct EagerResolver<'a, Infcx, I = <Infcx as SolverDelegate>::Interner>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     infcx: &'a Infcx,
 }
 
-impl<'a, Infcx: InferCtxtLike> EagerResolver<'a, Infcx> {
+impl<'a, Infcx: SolverDelegate> EagerResolver<'a, Infcx> {
     pub fn new(infcx: &'a Infcx) -> Self {
         EagerResolver { infcx }
     }
 }
 
-impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I> for EagerResolver<'_, Infcx> {
+impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for EagerResolver<'_, Infcx> {
     fn interner(&self) -> I {
         self.infcx.interner()
     }
diff --git a/compiler/rustc_next_trait_solver/src/solve.rs b/compiler/rustc_next_trait_solver/src/solve.rs
deleted file mode 100644
index eba96facabc..00000000000
--- a/compiler/rustc_next_trait_solver/src/solve.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub use rustc_type_ir::solve::*;
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
index 4e52caa5a5b..fbc8ac1d5d5 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/alias_relate.rs
@@ -15,17 +15,23 @@
 //! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
 //! relate them structurally.
 
-use super::EvalCtxt;
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
+use tracing::{instrument, trace};
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn compute_alias_relate_goal(
         &mut self,
-        goal: Goal<'tcx, (ty::Term<'tcx>, ty::Term<'tcx>, ty::AliasRelationDirection)>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, (I::Term, I::Term, ty::AliasRelationDirection)>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let Goal { param_env, predicate: (lhs, rhs, direction) } = goal;
         debug_assert!(lhs.to_alias_term().is_some() || rhs.to_alias_term().is_some());
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
index 1cdbf082078..9a1537d2606 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
@@ -1,33 +1,26 @@
 //! Code shared by trait and projection goals for candidate assembly.
 
-use derivative::Derivative;
-use rustc_hir::def_id::DefId;
-use rustc_hir::LangItem;
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::util::supertraits;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::inspect::ProbeKind;
-use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause, QueryResult};
-use rustc_middle::traits::BuiltinImplSource;
-use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{fast_reject, TypeFoldable};
-use rustc_middle::ty::{TypeVisitableExt, Upcast};
-use rustc_span::{ErrorGuaranteed, DUMMY_SP};
-use rustc_type_ir::solve::{CandidateSource, CanonicalResponse};
-use rustc_type_ir::Interner;
-
-use crate::solve::GoalSource;
-use crate::solve::{EvalCtxt, SolverMode};
-
 pub(super) mod structural_traits;
 
+use rustc_type_ir::fold::TypeFoldable;
+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, Upcast as _};
+use tracing::{debug, instrument};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, CanonicalResponse, Certainty, EvalCtxt, Goal, GoalSource,
+    MaybeCause, NoSolution, QueryResult, SolverMode,
+};
+
 /// A candidate is a possible way to prove a goal.
 ///
 /// It consists of both the `source`, which describes how that goal would be proven,
 /// and the `result` when using the given `source`.
-#[derive(Derivative)]
+#[derive(derivative::Derivative)]
 #[derivative(Debug(bound = ""), Clone(bound = ""))]
 pub(super) struct Candidate<I: Interner> {
     pub(super) source: CandidateSource<I>,
@@ -35,39 +28,42 @@ pub(super) struct Candidate<I: Interner> {
 }
 
 /// Methods used to assemble candidates for either trait or projection goals.
-pub(super) trait GoalKind<'tcx>:
-    TypeFoldable<TyCtxt<'tcx>> + Copy + Eq + std::fmt::Display
+pub(super) trait GoalKind<Infcx, I = <Infcx as SolverDelegate>::Interner>:
+    TypeFoldable<I> + Copy + Eq + std::fmt::Display
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
 {
-    fn self_ty(self) -> Ty<'tcx>;
+    fn self_ty(self) -> I::Ty;
 
-    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx>;
+    fn trait_ref(self, tcx: I) -> ty::TraitRef<I>;
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self;
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self;
 
-    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId;
+    fn trait_def_id(self, tcx: I) -> I::DefId;
 
     /// Try equating an assumption predicate against a goal's predicate. If it
     /// holds, then execute the `then` callback, which should do any additional
     /// work, then produce a response (typically by executing
     /// [`EvalCtxt::evaluate_added_goals_and_make_canonical_response`]).
     fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// Consider a clause, which consists of a "assumption" and some "requirements",
     /// to satisfy a goal. If the requirements hold, then attempt to satisfy our
     /// goal by equating it with the assumption.
     fn probe_and_consider_implied_clause(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        parent_source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        requirements: impl IntoIterator<Item = (GoalSource, Goal<'tcx, ty::Predicate<'tcx>>)>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        parent_source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        requirements: impl IntoIterator<Item = (GoalSource, Goal<I, I::Predicate>)>,
+    ) -> Result<Candidate<I>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, parent_source, goal, assumption, |ecx| {
             for (nested_source, goal) in requirements {
                 ecx.add_goal(nested_source, goal);
@@ -80,15 +76,15 @@ pub(super) trait GoalKind<'tcx>:
     /// additionally checking all of the supertraits and object bounds to hold,
     /// since they're not implied by the well-formedness of the object type.
     fn probe_and_consider_object_bound_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<TyCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+    ) -> Result<Candidate<I>, NoSolution> {
         Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
             let tcx = ecx.interner();
-            let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
-                bug!("expected object type in `probe_and_consider_object_bound_candidate`");
+            let ty::Dynamic(bounds, _, _) = goal.predicate.self_ty().kind() else {
+                panic!("expected object type in `probe_and_consider_object_bound_candidate`");
             };
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
@@ -104,10 +100,10 @@ pub(super) trait GoalKind<'tcx>:
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// If the predicate contained an error, we want to avoid emitting unnecessary trait
     /// errors but still want to emit errors for other trait goals. We have some special
@@ -116,85 +112,85 @@ pub(super) trait GoalKind<'tcx>:
     /// Trait goals always hold while projection goals never do. This is a bit arbitrary
     /// but prevents incorrect normalization while hiding any trait errors.
     fn consider_error_guaranteed_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type implements an `auto trait` if its components do as well.
     ///
     /// These components are given by built-in rules from
     /// [`structural_traits::instantiate_constituent_tys_for_auto_trait`].
     fn consider_auto_trait_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A trait alias holds if the RHS traits and `where` clauses hold.
     fn consider_trait_alias_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is `Sized` if its tail component is `Sized`.
     ///
     /// These components are given by built-in rules from
     /// [`structural_traits::instantiate_constituent_tys_for_sized_trait`].
     fn consider_builtin_sized_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is `Copy` or `Clone` if its components are `Copy` or `Clone`.
     ///
     /// These components are given by built-in rules from
     /// [`structural_traits::instantiate_constituent_tys_for_copy_clone_trait`].
     fn consider_builtin_copy_clone_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is `PointerLike` if we can compute its layout, and that layout
     /// matches the layout of `usize`.
     fn consider_builtin_pointer_like_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A type is a `FnPtr` if it is of `FnPtr` type.
     fn consider_builtin_fn_ptr_trait_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A callable type (a closure, fn def, or fn ptr) is known to implement the `Fn<A>`
     /// family of traits where `A` is given by the signature of the type.
     fn consider_builtin_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// An async closure is known to implement the `AsyncFn<A>` family of traits
     /// where `A` is given by the signature of the type.
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which
     /// is used internally to delay computation for async closures until after
     /// upvar analysis is performed in HIR typeck.
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// `Tuple` is implemented if the `Self` type is a tuple.
     fn consider_builtin_tuple_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// `Pointee` is always implemented.
     ///
@@ -202,65 +198,65 @@ pub(super) trait GoalKind<'tcx>:
     /// the built-in types. For structs, the metadata type is given by the struct
     /// tail.
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A coroutine (that comes from an `async` desugaring) is known to implement
     /// `Future<Output = O>`, where `O` is given by the coroutine's return type
     /// that was computed during type-checking.
     fn consider_builtin_future_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A coroutine (that comes from a `gen` desugaring) is known to implement
     /// `Iterator<Item = O>`, where `O` is given by the generator's yield type
     /// that was computed during type-checking.
     fn consider_builtin_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A coroutine (that comes from a `gen` desugaring) is known to implement
     /// `FusedIterator`
     fn consider_builtin_fused_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to
     /// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield,
     /// and return types of the coroutine computed during type-checking.
     fn consider_builtin_coroutine_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     fn consider_builtin_transmute_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution>;
 
     /// Consider (possibly several) candidates to upcast or unsize a type to another
     /// type, excluding the coercion of a sized type into a `dyn Trait`.
@@ -270,16 +266,20 @@ pub(super) trait GoalKind<'tcx>:
     /// otherwise recompute this for codegen. This is a bit of a mess but the
     /// easiest way to maintain the existing behavior for now.
     fn consider_structural_builtin_unsize_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>>;
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>>;
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
-    pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<'tcx>>(
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    pub(super) fn assemble_and_evaluate_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
+        goal: Goal<I, G>,
+    ) -> Vec<Candidate<I>> {
         let Ok(normalized_self_ty) =
             self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
@@ -291,7 +291,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             return self.forced_ambiguity(MaybeCause::Ambiguity).into_iter().collect();
         }
 
-        let goal: Goal<'tcx, G> = goal.with(
+        let goal: Goal<I, G> = goal.with(
             self.interner(),
             goal.predicate.with_self_ty(self.interner(), normalized_self_ty),
         );
@@ -301,7 +301,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
         let mut candidates = vec![];
 
-        self.assemble_non_blanket_impl_candidates(goal, &mut candidates);
+        self.assemble_impl_candidates(goal, &mut candidates);
 
         self.assemble_builtin_impl_candidates(goal, &mut candidates);
 
@@ -309,8 +309,6 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
         self.assemble_object_bound_candidates(goal, &mut candidates);
 
-        self.assemble_blanket_impl_candidates(goal, &mut candidates);
-
         self.assemble_param_env_candidates(goal, &mut candidates);
 
         match self.solver_mode() {
@@ -326,7 +324,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(super) fn forced_ambiguity(
         &mut self,
         cause: MaybeCause,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         // This may fail if `try_evaluate_added_goals` overflows because it
         // fails to reach a fixpoint but ends up getting an error after
         // running for some additional step.
@@ -339,149 +337,36 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
+    fn assemble_impl_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        let self_ty = goal.predicate.self_ty();
-        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
-        let mut consider_impls_for_simplified_type = |simp| {
-            if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
-                for &impl_def_id in impls_for_type {
-                    // For every `default impl`, there's always a non-default `impl`
-                    // that will *also* apply. There's no reason to register a candidate
-                    // for this impl, since it is *not* proof that the trait goal holds.
-                    if tcx.defaultness(impl_def_id).is_default() {
-                        return;
-                    }
-
-                    match G::consider_impl_candidate(self, goal, impl_def_id) {
-                        Ok(candidate) => candidates.push(candidate),
-                        Err(NoSolution) => (),
-                    }
-                }
-            }
-        };
-
-        match self_ty.kind() {
-            ty::Bool
-            | ty::Char
-            | ty::Int(_)
-            | ty::Uint(_)
-            | ty::Float(_)
-            | ty::Adt(_, _)
-            | ty::Foreign(_)
-            | ty::Str
-            | ty::Array(_, _)
-            | ty::Pat(_, _)
-            | ty::Slice(_)
-            | ty::RawPtr(_, _)
-            | ty::Ref(_, _, _)
-            | ty::FnDef(_, _)
-            | ty::FnPtr(_)
-            | ty::Dynamic(_, _, _)
-            | ty::Closure(..)
-            | ty::CoroutineClosure(..)
-            | ty::Coroutine(_, _)
-            | ty::Never
-            | ty::Tuple(_) => {
-                let simp =
-                    fast_reject::simplify_type(tcx, self_ty, TreatParams::ForLookup).unwrap();
-                consider_impls_for_simplified_type(simp);
-            }
-
-            // HACK: For integer and float variables we have to manually look at all impls
-            // which have some integer or float as a self type.
-            ty::Infer(ty::IntVar(_)) => {
-                use ty::IntTy::*;
-                use ty::UintTy::*;
-                // This causes a compiler error if any new integer kinds are added.
-                let (I8 | I16 | I32 | I64 | I128 | Isize): ty::IntTy;
-                let (U8 | U16 | U32 | U64 | U128 | Usize): ty::UintTy;
-                let possible_integers = [
-                    // signed integers
-                    SimplifiedType::Int(I8),
-                    SimplifiedType::Int(I16),
-                    SimplifiedType::Int(I32),
-                    SimplifiedType::Int(I64),
-                    SimplifiedType::Int(I128),
-                    SimplifiedType::Int(Isize),
-                    // unsigned integers
-                    SimplifiedType::Uint(U8),
-                    SimplifiedType::Uint(U16),
-                    SimplifiedType::Uint(U32),
-                    SimplifiedType::Uint(U64),
-                    SimplifiedType::Uint(U128),
-                    SimplifiedType::Uint(Usize),
-                ];
-                for simp in possible_integers {
-                    consider_impls_for_simplified_type(simp);
+        tcx.for_each_relevant_impl(
+            goal.predicate.trait_def_id(tcx),
+            goal.predicate.self_ty(),
+            |impl_def_id| {
+                // For every `default impl`, there's always a non-default `impl`
+                // that will *also* apply. There's no reason to register a candidate
+                // for this impl, since it is *not* proof that the trait goal holds.
+                if tcx.impl_is_default(impl_def_id) {
+                    return;
                 }
-            }
 
-            ty::Infer(ty::FloatVar(_)) => {
-                // This causes a compiler error if any new float kinds are added.
-                let (ty::FloatTy::F16 | ty::FloatTy::F32 | ty::FloatTy::F64 | ty::FloatTy::F128);
-                let possible_floats = [
-                    SimplifiedType::Float(ty::FloatTy::F16),
-                    SimplifiedType::Float(ty::FloatTy::F32),
-                    SimplifiedType::Float(ty::FloatTy::F64),
-                    SimplifiedType::Float(ty::FloatTy::F128),
-                ];
-
-                for simp in possible_floats {
-                    consider_impls_for_simplified_type(simp);
+                match G::consider_impl_candidate(self, goal, impl_def_id) {
+                    Ok(candidate) => candidates.push(candidate),
+                    Err(NoSolution) => (),
                 }
-            }
-
-            // The only traits applying to aliases and placeholders are blanket impls.
-            //
-            // Impls which apply to an alias after normalization are handled by
-            // `assemble_candidates_after_normalizing_self_ty`.
-            ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (),
-
-            // FIXME: These should ideally not exist as a self type. It would be nice for
-            // the builtin auto trait impls of coroutines to instead directly recurse
-            // into the witness.
-            ty::CoroutineWitness(..) => (),
-
-            // These variants should not exist as a self type.
-            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Param(_)
-            | ty::Bound(_, _) => bug!("unexpected self type: {self_ty}"),
-        }
-    }
-
-    #[instrument(level = "trace", skip_all)]
-    fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
-        &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
-    ) {
-        let tcx = self.interner();
-        let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
-        for &impl_def_id in trait_impls.blanket_impls() {
-            // For every `default impl`, there's always a non-default `impl`
-            // that will *also* apply. There's no reason to register a candidate
-            // for this impl, since it is *not* proof that the trait goal holds.
-            if tcx.defaultness(impl_def_id).is_default() {
-                return;
-            }
-
-            match G::consider_impl_candidate(self, goal, impl_def_id) {
-                Ok(candidate) => candidates.push(candidate),
-                Err(NoSolution) => (),
-            }
-        }
+            },
+        );
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
+    fn assemble_builtin_impl_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
         let trait_def_id = goal.predicate.trait_def_id(tcx);
@@ -499,43 +384,43 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             G::consider_auto_trait_candidate(self, goal)
         } else if tcx.trait_is_alias(trait_def_id) {
             G::consider_trait_alias_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Sized) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Sized) {
             G::consider_builtin_sized_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Copy)
-            || tcx.is_lang_item(trait_def_id, LangItem::Clone)
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Copy)
+            || tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Clone)
         {
             G::consider_builtin_copy_clone_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::PointerLike) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointerLike) {
             G::consider_builtin_pointer_like_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::FnPtrTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FnPtrTrait) {
             G::consider_builtin_fn_ptr_trait_candidate(self, goal)
         } else if let Some(kind) = self.interner().fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_fn_trait_candidates(self, goal, kind)
         } else if let Some(kind) = self.interner().async_fn_trait_kind_from_def_id(trait_def_id) {
             G::consider_builtin_async_fn_trait_candidates(self, goal, kind)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncFnKindHelper) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncFnKindHelper) {
             G::consider_builtin_async_fn_kind_helper_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Tuple) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Tuple) {
             G::consider_builtin_tuple_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::PointeeTrait) {
             G::consider_builtin_pointee_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Future) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Future) {
             G::consider_builtin_future_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Iterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Iterator) {
             G::consider_builtin_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::FusedIterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::FusedIterator) {
             G::consider_builtin_fused_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncIterator) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncIterator) {
             G::consider_builtin_async_iterator_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Coroutine) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Coroutine) {
             G::consider_builtin_coroutine_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::DiscriminantKind) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::DiscriminantKind) {
             G::consider_builtin_discriminant_kind_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::AsyncDestruct) {
             G::consider_builtin_async_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::Destruct) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Destruct) {
             G::consider_builtin_destruct_candidate(self, goal)
-        } else if tcx.is_lang_item(trait_def_id, LangItem::TransmuteTrait) {
+        } else if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::TransmuteTrait) {
             G::consider_builtin_transmute_candidate(self, goal)
         } else {
             Err(NoSolution)
@@ -545,18 +430,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
         // There may be multiple unsize candidates for a trait with several supertraits:
         // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
-        if tcx.is_lang_item(trait_def_id, LangItem::Unsize) {
+        if tcx.is_lang_item(trait_def_id, TraitSolverLangItem::Unsize) {
             candidates.extend(G::consider_structural_builtin_unsize_candidates(self, goal));
         }
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_param_env_candidates<G: GoalKind<'tcx>>(
+    fn assemble_param_env_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
-        for (i, assumption) in goal.param_env.caller_bounds().iter().enumerate() {
+        for (i, assumption) in goal.param_env.caller_bounds().into_iter().enumerate() {
             candidates.extend(G::probe_and_consider_implied_clause(
                 self,
                 CandidateSource::ParamEnv(i),
@@ -568,10 +453,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_alias_bound_candidates<G: GoalKind<'tcx>>(
+    fn assemble_alias_bound_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| {
             ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates);
@@ -587,13 +472,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// If so, continue searching by recursively calling after normalization.
     // FIXME: This may recurse infinitely, but I can't seem to trigger it without
     // hitting another overflow error something. Add a depth parameter needed later.
-    fn assemble_alias_bound_candidates_recur<G: GoalKind<'tcx>>(
+    fn assemble_alias_bound_candidates_recur<G: GoalKind<Infcx>>(
         &mut self,
-        self_ty: Ty<'tcx>,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        self_ty: I::Ty,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
-        let (kind, alias_ty) = match *self_ty.kind() {
+        let (kind, alias_ty) = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -621,7 +506,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Error(_) => return,
             ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) | ty::Bound(..) => {
-                bug!("unexpected self type for `{goal:?}`")
+                panic!("unexpected self type for `{goal:?}`")
             }
 
             ty::Infer(ty::TyVar(_)) => {
@@ -638,16 +523,15 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
             ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
             ty::Alias(ty::Inherent | ty::Weak, _) => {
-                self.interner().sess.dcx().span_delayed_bug(
-                    DUMMY_SP,
-                    format!("could not normalize {self_ty}, it is not WF"),
-                );
+                self.interner().delay_bug(format!("could not normalize {self_ty:?}, it is not WF"));
                 return;
             }
         };
 
-        for assumption in
-            self.interner().item_bounds(alias_ty.def_id).instantiate(self.interner(), alias_ty.args)
+        for assumption in self
+            .interner()
+            .item_bounds(alias_ty.def_id)
+            .iter_instantiated(self.interner(), &alias_ty.args)
         {
             candidates.extend(G::probe_and_consider_implied_clause(
                 self,
@@ -672,18 +556,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip_all)]
-    fn assemble_object_bound_candidates<G: GoalKind<'tcx>>(
+    fn assemble_object_bound_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        if !tcx.trait_def(goal.predicate.trait_def_id(tcx)).implement_via_object {
+        if !tcx.trait_may_be_implemented_via_object(goal.predicate.trait_def_id(tcx)) {
             return;
         }
 
         let self_ty = goal.predicate.self_ty();
-        let bounds = match *self_ty.kind() {
+        let bounds = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(_)
@@ -711,12 +595,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             | ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
             | ty::Error(_) => return,
             ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Bound(..) => bug!("unexpected self type for `{goal:?}`"),
+            | ty::Bound(..) => panic!("unexpected self type for `{goal:?}`"),
             ty::Dynamic(bounds, ..) => bounds,
         };
 
         // Do not consider built-in object impls for non-object-safe types.
-        if bounds.principal_def_id().is_some_and(|def_id| !tcx.is_object_safe(def_id)) {
+        if bounds.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
             return;
         }
 
@@ -745,7 +629,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         // a projection goal.
         if let Some(principal) = bounds.principal() {
             let principal_trait_ref = principal.with_self_ty(tcx, self_ty);
-            for (idx, assumption) in supertraits(self.interner(), principal_trait_ref).enumerate() {
+            for (idx, assumption) in
+                Infcx::elaborate_supertraits(tcx, principal_trait_ref).enumerate()
+            {
                 candidates.extend(G::probe_and_consider_object_bound_candidate(
                     self,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::Object(idx)),
@@ -763,10 +649,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// To do so we add an ambiguous candidate in case such an unknown impl could
     /// apply to the current goal.
     #[instrument(level = "trace", skip_all)]
-    fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
+    fn assemble_coherence_unknowable_candidates<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
 
@@ -792,13 +678,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     // to improve this however. However, this should make it fairly straightforward to refine
     // the filtering going forward, so it seems alright-ish for now.
     #[instrument(level = "debug", skip(self, goal))]
-    fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
+    fn discard_impls_shadowed_by_env<G: GoalKind<Infcx>>(
         &mut self,
-        goal: Goal<'tcx, G>,
-        candidates: &mut Vec<Candidate<TyCtxt<'tcx>>>,
+        goal: Goal<I, G>,
+        candidates: &mut Vec<Candidate<I>>,
     ) {
         let tcx = self.interner();
-        let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
+        let trait_goal: Goal<I, ty::TraitPredicate<I>> =
             goal.with(tcx, goal.predicate.trait_ref(tcx));
 
         let mut trait_candidates_from_env = vec![];
@@ -823,7 +709,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                             false
                         }
                         CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
-                        CandidateSource::CoherenceUnknowable => bug!("uh oh"),
+                        CandidateSource::CoherenceUnknowable => panic!("uh oh"),
                     });
                 }
                 // If it is still ambiguous we instead just force the whole goal
@@ -841,10 +727,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// to somehow try to merge the candidates into one. If that fails, we return
     /// ambiguity.
     #[instrument(level = "debug", skip(self), ret)]
-    pub(super) fn merge_candidates(
-        &mut self,
-        candidates: Vec<Candidate<TyCtxt<'tcx>>>,
-    ) -> QueryResult<'tcx> {
+    pub(super) fn merge_candidates(&mut self, candidates: Vec<Candidate<I>>) -> QueryResult<I> {
         // First try merging all candidates. This is complete and fully sound.
         let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
         if let Some(result) = self.try_merge_responses(&responses) {
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
index d6074617caf..202af76565a 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/assembly/structural_traits.rs
@@ -3,26 +3,24 @@
 
 use rustc_ast_ir::{Movability, Mutability};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_next_trait_solver::solve::{Goal, NoSolution};
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::lang_items::TraitSolverLangItem;
-use rustc_type_ir::{self as ty, InferCtxtLike, Interner, Upcast};
+use rustc_type_ir::{self as ty, Interner, Upcast as _};
 use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
+use tracing::instrument;
 
-use crate::solve::EvalCtxt;
+use crate::infcx::SolverDelegate;
+use crate::solve::{EvalCtxt, Goal, NoSolution};
 
 // Calculates the constituent types of a type for `auto trait` purposes.
-//
-// For types with an "existential" binder, i.e. coroutine witnesses, we also
-// instantiate the binder with placeholders eagerly.
 #[instrument(level = "trace", skip(ecx), ret)]
 pub(in crate::solve) fn instantiate_constituent_tys_for_auto_trait<Infcx, I>(
     ecx: &EvalCtxt<'_, Infcx>,
     ty: I::Ty,
 ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     let tcx = ecx.interner();
@@ -108,7 +106,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_sized_trait<Infcx, I>(
     ty: I::Ty,
 ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     match ty.kind() {
@@ -176,7 +174,7 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<Infcx,
     ty: I::Ty,
 ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     match ty.kind() {
@@ -663,7 +661,7 @@ pub(in crate::solve) fn predicates_for_object_candidate<Infcx, I>(
     object_bounds: I::BoundExistentialPredicates,
 ) -> Vec<Goal<I, I::Predicate>>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     let tcx = ecx.interner();
@@ -712,14 +710,14 @@ where
         .collect()
 }
 
-struct ReplaceProjectionWith<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
+struct ReplaceProjectionWith<'a, Infcx: SolverDelegate<Interner = I>, I: Interner> {
     ecx: &'a EvalCtxt<'a, Infcx>,
     param_env: I::ParamEnv,
     mapping: FxHashMap<I::DefId, ty::Binder<I, ty::ProjectionPredicate<I>>>,
     nested: Vec<Goal<I, I::Predicate>>,
 }
 
-impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
+impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I>
     for ReplaceProjectionWith<'_, Infcx, I>
 {
     fn interner(&self) -> I {
@@ -744,7 +742,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
                     )
                     .expect("expected to be able to unify goal projection with dyn's projection"),
             );
-            proj.term.expect_type()
+            proj.term.expect_ty()
         } else {
             ty.super_fold_with(self)
         }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
index b5753d60f59..c6611285a3b 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs
@@ -8,58 +8,53 @@
 //! section of the [rustc-dev-guide][c].
 //!
 //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
-use super::{CanonicalInput, Certainty, EvalCtxt, Goal};
+
+use 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 tracing::{instrument, trace};
+
+use crate::canonicalizer::{CanonicalizeMode, Canonicalizer};
+use crate::infcx::SolverDelegate;
+use crate::resolve::EagerResolver;
 use crate::solve::eval_ctxt::NestedGoals;
+use crate::solve::inspect;
 use crate::solve::{
-    inspect, response_no_constraints_raw, CanonicalResponse, QueryResult, Response,
-};
-use rustc_data_structures::fx::FxHashSet;
-use rustc_index::IndexVec;
-use rustc_infer::infer::canonical::query_response::make_query_region_constraints;
-use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints};
-use rustc_infer::infer::RegionVariableOrigin;
-use rustc_infer::infer::{InferCtxt, InferOk};
-use rustc_infer::traits::solve::NestedNormalizationGoals;
-use rustc_middle::bug;
-use rustc_middle::infer::canonical::Canonical;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::{
-    ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput,
+    response_no_constraints_raw, CanonicalInput, CanonicalResponse, Certainty, EvalCtxt,
+    ExternalConstraintsData, Goal, MaybeCause, NestedNormalizationGoals, NoSolution,
+    PredefinedOpaquesData, QueryInput, QueryResult, Response,
 };
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable};
-use rustc_next_trait_solver::canonicalizer::{CanonicalizeMode, Canonicalizer};
-use rustc_next_trait_solver::resolve::EagerResolver;
-use rustc_span::{Span, DUMMY_SP};
-use rustc_type_ir::CanonicalVarValues;
-use rustc_type_ir::{InferCtxtLike, Interner};
-use std::assert_matches::assert_matches;
-use std::iter;
-use std::ops::Deref;
 
-trait ResponseT<'tcx> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>>;
+trait ResponseT<I: Interner> {
+    fn var_values(&self) -> CanonicalVarValues<I>;
 }
 
-impl<'tcx> ResponseT<'tcx> for Response<TyCtxt<'tcx>> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
+impl<I: Interner> ResponseT<I> for Response<I> {
+    fn var_values(&self) -> CanonicalVarValues<I> {
         self.var_values
     }
 }
 
-impl<'tcx, T> ResponseT<'tcx> for inspect::State<TyCtxt<'tcx>, T> {
-    fn var_values(&self) -> CanonicalVarValues<TyCtxt<'tcx>> {
+impl<I: Interner, T> ResponseT<I> for inspect::State<I, T> {
+    fn var_values(&self) -> CanonicalVarValues<I> {
         self.var_values
     }
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Canonicalizes the goal remembering the original values
     /// for each bound variable.
-    pub(super) fn canonicalize_goal<T: TypeFoldable<TyCtxt<'tcx>>>(
+    pub(super) fn canonicalize_goal<T: TypeFoldable<I>>(
         &self,
-        goal: Goal<'tcx, T>,
-    ) -> (Vec<ty::GenericArg<'tcx>>, CanonicalInput<'tcx, T>) {
+        goal: Goal<I, T>,
+    ) -> (Vec<I::GenericArg>, CanonicalInput<I, T>) {
         let opaque_types = self.infcx.clone_opaque_types_for_query_response();
         let (goal, opaque_types) =
             (goal, opaque_types).fold_with(&mut EagerResolver::new(self.infcx));
@@ -89,7 +84,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response(
         &mut self,
         certainty: Certainty,
-    ) -> QueryResult<'tcx> {
+    ) -> QueryResult<I> {
         self.inspect.make_canonical_response(certainty);
 
         let goals_certainty = self.try_evaluate_added_goals()?;
@@ -102,8 +97,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
         // We only check for leaks from universes which were entered inside
         // of the query.
-        self.infcx.leak_check(self.max_input_universe, None).map_err(|e| {
-            trace!(?e, "failed the leak check");
+        self.infcx.leak_check(self.max_input_universe).map_err(|NoSolution| {
+            trace!("failed the leak check");
             NoSolution
         })?;
 
@@ -119,7 +114,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             if cfg!(debug_assertions) {
                 assert!(normalizes_to_goals.is_empty());
                 if goals.is_empty() {
-                    assert_matches!(goals_certainty, Certainty::Yes);
+                    assert!(matches!(goals_certainty, Certainty::Yes));
                 }
             }
             (certainty, NestedNormalizationGoals(goals))
@@ -158,7 +153,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     pub(in crate::solve) fn make_ambiguous_response_no_constraints(
         &self,
         maybe_cause: MaybeCause,
-    ) -> CanonicalResponse<'tcx> {
+    ) -> CanonicalResponse<I> {
         response_no_constraints_raw(
             self.interner(),
             self.max_input_universe,
@@ -178,8 +173,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     fn compute_external_query_constraints(
         &self,
         certainty: Certainty,
-        normalization_nested_goals: NestedNormalizationGoals<TyCtxt<'tcx>>,
-    ) -> ExternalConstraintsData<TyCtxt<'tcx>> {
+        normalization_nested_goals: NestedNormalizationGoals<I>,
+    ) -> ExternalConstraintsData<I> {
         // We only return region constraints once the certainty is `Yes`. This
         // is necessary as we may drop nested goals on ambiguity, which may result
         // in unconstrained inference variables in the region constraints. It also
@@ -189,26 +184,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-5-ambig.rs` and
         // `tests/ui/higher-ranked/leak-check/leak-check-in-selection-6-ambig-unify.rs`.
         let region_constraints = if certainty == Certainty::Yes {
-            // Cannot use `take_registered_region_obligations` as we may compute the response
-            // inside of a `probe` whenever we have multiple choices inside of the solver.
-            let region_obligations = self.infcx.inner.borrow().region_obligations().to_owned();
-            let QueryRegionConstraints { outlives, member_constraints } =
-                self.infcx.with_region_constraints(|region_constraints| {
-                    make_query_region_constraints(
-                        self.interner(),
-                        region_obligations.iter().map(|r_o| {
-                            (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())
-                        }),
-                        region_constraints,
-                    )
-                });
-            assert_eq!(member_constraints, vec![]);
-            let mut seen = FxHashSet::default();
-            outlives
-                .into_iter()
-                .filter(|(outlives, _)| seen.insert(*outlives))
-                .map(|(outlives, _origin)| outlives)
-                .collect()
+            self.infcx.make_deduplicated_outlives_constraints()
         } else {
             Default::default()
         };
@@ -238,10 +214,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     ///   the `normalization_nested_goals`
     pub(super) fn instantiate_and_apply_query_response(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: Vec<ty::GenericArg<'tcx>>,
-        response: CanonicalResponse<'tcx>,
-    ) -> (NestedNormalizationGoals<TyCtxt<'tcx>>, Certainty) {
+        param_env: I::ParamEnv,
+        original_values: Vec<I::GenericArg>,
+        response: CanonicalResponse<I>,
+    ) -> (NestedNormalizationGoals<I>, Certainty) {
         let instantiation = Self::compute_query_response_instantiation_values(
             self.infcx,
             &original_values,
@@ -249,7 +225,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         );
 
         let Response { var_values, external_constraints, certainty } =
-            response.instantiate(self.interner(), &instantiation);
+            self.infcx.instantiate_canonical(response, instantiation);
 
         Self::unify_query_var_values(self.infcx, param_env, &original_values, var_values);
 
@@ -257,7 +233,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             region_constraints,
             opaque_types,
             normalization_nested_goals,
-        } = external_constraints.deref();
+        } = &*external_constraints;
         self.register_region_constraints(region_constraints);
         self.register_new_opaque_types(opaque_types);
         (normalization_nested_goals.clone(), certainty)
@@ -266,11 +242,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// This returns the canoncial variable values to instantiate the bound variables of
     /// the canonical response. This depends on the `original_values` for the
     /// bound variables.
-    fn compute_query_response_instantiation_values<T: ResponseT<'tcx>>(
-        infcx: &InferCtxt<'tcx>,
-        original_values: &[ty::GenericArg<'tcx>],
-        response: &Canonical<'tcx, T>,
-    ) -> CanonicalVarValues<TyCtxt<'tcx>> {
+    fn compute_query_response_instantiation_values<T: ResponseT<I>>(
+        infcx: &Infcx,
+        original_values: &[I::GenericArg],
+        response: &Canonical<I, T>,
+    ) -> CanonicalVarValues<I> {
         // FIXME: Longterm canonical queries should deal with all placeholders
         // created inside of the query directly instead of returning them to the
         // caller.
@@ -292,35 +268,35 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         // inference variable of the input right away, which is more performant.
         let mut opt_values = IndexVec::from_elem_n(None, response.variables.len());
         for (original_value, result_value) in iter::zip(original_values, var_values.var_values) {
-            match result_value.unpack() {
-                GenericArgKind::Type(t) => {
-                    if let &ty::Bound(debruijn, b) = t.kind() {
+            match result_value.kind() {
+                ty::GenericArgKind::Type(t) => {
+                    if let ty::Bound(debruijn, b) = t.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[b.var] = Some(*original_value);
+                        opt_values[b.var()] = Some(*original_value);
                     }
                 }
-                GenericArgKind::Lifetime(r) => {
-                    if let ty::ReBound(debruijn, br) = *r {
+                ty::GenericArgKind::Lifetime(r) => {
+                    if let ty::ReBound(debruijn, br) = r.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[br.var] = Some(*original_value);
+                        opt_values[br.var()] = Some(*original_value);
                     }
                 }
-                GenericArgKind::Const(c) => {
-                    if let ty::ConstKind::Bound(debruijn, b) = c.kind() {
+                ty::GenericArgKind::Const(c) => {
+                    if let ty::ConstKind::Bound(debruijn, bv) = c.kind() {
                         assert_eq!(debruijn, ty::INNERMOST);
-                        opt_values[b] = Some(*original_value);
+                        opt_values[bv.var()] = Some(*original_value);
                     }
                 }
             }
         }
 
-        let var_values = infcx.tcx.mk_args_from_iter(response.variables.iter().enumerate().map(
-            |(index, info)| {
+        let var_values = infcx.interner().mk_args_from_iter(
+            response.variables.into_iter().enumerate().map(|(index, info)| {
                 if info.universe() != ty::UniverseIndex::ROOT {
                     // A variable from inside a binder of the query. While ideally these shouldn't
                     // exist at all (see the FIXME at the start of this method), we have to deal with
                     // them for now.
-                    infcx.instantiate_canonical_var(DUMMY_SP, info, |idx| {
+                    infcx.instantiate_canonical_var_with_infer(info, |idx| {
                         ty::UniverseIndex::from(prev_universe.index() + idx.index())
                     })
                 } else if info.is_existential() {
@@ -331,18 +307,18 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     // more placeholders then they should be able to. However the inference variables have
                     // to "come from somewhere", so by equating them with the original values of the caller
                     // later on, we pull them down into their correct universe again.
-                    if let Some(v) = opt_values[BoundVar::from_usize(index)] {
+                    if let Some(v) = opt_values[ty::BoundVar::from_usize(index)] {
                         v
                     } else {
-                        infcx.instantiate_canonical_var(DUMMY_SP, info, |_| prev_universe)
+                        infcx.instantiate_canonical_var_with_infer(info, |_| prev_universe)
                     }
                 } else {
                     // For placeholders which were already part of the input, we simply map this
                     // universal bound variable back the placeholder of the input.
                     original_values[info.expect_placeholder_index()]
                 }
-            },
-        ));
+            }),
+        );
 
         CanonicalVarValues { var_values }
     }
@@ -361,40 +337,35 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// always relate them structurally here.
     #[instrument(level = "trace", skip(infcx))]
     fn unify_query_var_values(
-        infcx: &InferCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: &[ty::GenericArg<'tcx>],
-        var_values: CanonicalVarValues<TyCtxt<'tcx>>,
+        infcx: &Infcx,
+        param_env: I::ParamEnv,
+        original_values: &[I::GenericArg],
+        var_values: CanonicalVarValues<I>,
     ) {
         assert_eq!(original_values.len(), var_values.len());
 
-        let cause = ObligationCause::dummy();
         for (&orig, response) in iter::zip(original_values, var_values.var_values) {
-            let InferOk { value: (), obligations } = infcx
-                .at(&cause, param_env)
-                .eq_structurally_relating_aliases(orig, response)
-                .unwrap();
-            assert!(obligations.is_empty());
+            let goals = infcx.eq_structurally_relating_aliases(param_env, orig, response).unwrap();
+            assert!(goals.is_empty());
         }
     }
 
     fn register_region_constraints(
         &mut self,
-        outlives: &[ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>],
+        outlives: &[ty::OutlivesPredicate<I, I::GenericArg>],
     ) {
         for &ty::OutlivesPredicate(lhs, rhs) in outlives {
-            match lhs.unpack() {
-                GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs),
-                GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs),
-                GenericArgKind::Const(_) => bug!("const outlives: {lhs:?}: {rhs:?}"),
+            match lhs.kind() {
+                ty::GenericArgKind::Lifetime(lhs) => self.register_region_outlives(lhs, rhs),
+                ty::GenericArgKind::Type(lhs) => self.register_ty_outlives(lhs, rhs),
+                ty::GenericArgKind::Const(_) => panic!("const outlives: {lhs:?}: {rhs:?}"),
             }
         }
     }
 
-    fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)]) {
+    fn register_new_opaque_types(&mut self, opaque_types: &[(ty::OpaqueTypeKey<I>, I::Ty)]) {
         for &(key, ty) in opaque_types {
-            let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
-            self.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
+            self.infcx.inject_new_hidden_type_unchecked(key, ty);
         }
     }
 }
@@ -410,7 +381,7 @@ pub(in crate::solve) fn make_canonical_state<Infcx, T, I>(
     data: T,
 ) -> inspect::CanonicalState<I, T>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
     T: TypeFoldable<I>,
 {
@@ -425,45 +396,33 @@ where
     )
 }
 
-/// Instantiate a `CanonicalState`.
-///
-/// Unlike for query responses, `CanonicalState` also track fresh inference
-/// variables created while evaluating a goal. When creating two separate
-/// `CanonicalState` during a single evaluation both may reference this
-/// fresh inference variable. When instantiating them we now create separate
-/// inference variables for it and have to unify them somehow. We do this
-/// by extending the `var_values` while building the proof tree.
-///
-/// This currently assumes that unifying the var values trivially succeeds.
-/// Adding any inference constraints which weren't present when originally
-/// computing the canonical query can result in bugs.
-#[instrument(level = "trace", skip(infcx, span, param_env))]
-pub(in crate::solve) fn instantiate_canonical_state<'tcx, T: TypeFoldable<TyCtxt<'tcx>>>(
-    infcx: &InferCtxt<'tcx>,
-    span: Span,
-    param_env: ty::ParamEnv<'tcx>,
-    orig_values: &mut Vec<ty::GenericArg<'tcx>>,
-    state: inspect::CanonicalState<TyCtxt<'tcx>, T>,
-) -> T {
+// FIXME: needs to be pub to be accessed by downstream
+// `rustc_trait_selection::solve::inspect::analyse`.
+pub fn instantiate_canonical_state<Infcx, I, T: TypeFoldable<I>>(
+    infcx: &Infcx,
+    span: Infcx::Span,
+    param_env: I::ParamEnv,
+    orig_values: &mut Vec<I::GenericArg>,
+    state: inspect::CanonicalState<I, T>,
+) -> T
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     // In case any fresh inference variables have been created between `state`
     // and the previous instantiation, extend `orig_values` for it.
     assert!(orig_values.len() <= state.value.var_values.len());
-    for i in orig_values.len()..state.value.var_values.len() {
-        let unconstrained = match state.value.var_values.var_values[i].unpack() {
-            ty::GenericArgKind::Lifetime(_) => {
-                infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)).into()
-            }
-            ty::GenericArgKind::Type(_) => infcx.next_ty_var(span).into(),
-            ty::GenericArgKind::Const(_) => infcx.next_const_var(span).into(),
-        };
-
+    for &arg in &state.value.var_values.var_values[orig_values.len()..state.value.var_values.len()]
+    {
+        // FIXME: This is so ugly.
+        let unconstrained = infcx.fresh_var_for_kind_with_span(arg, span);
         orig_values.push(unconstrained);
     }
 
     let instantiation =
         EvalCtxt::compute_query_response_instantiation_values(infcx, orig_values, &state);
 
-    let inspect::State { var_values, data } = state.instantiate(infcx.tcx, &instantiation);
+    let inspect::State { var_values, data } = infcx.instantiate_canonical(state, instantiation);
 
     EvalCtxt::unify_query_var_values(infcx, param_env, orig_values, var_values);
     data
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 74938d2bbd7..485758b91a2 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -1,41 +1,30 @@
+use std::ops::ControlFlow;
+
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::{MaybeCause, NestedNormalizationGoals};
-use rustc_infer::traits::ObligationCause;
-use rustc_macros::{extension, HashStable, HashStable_NoContext, TyDecodable, TyEncodable};
-use rustc_middle::bug;
-use rustc_middle::traits::solve::{
-    inspect, CanonicalInput, CanonicalResponse, Certainty, PredefinedOpaquesData, QueryResult,
-};
-use rustc_middle::ty::AliasRelationDirection;
-use rustc_middle::ty::TypeFolder;
-use rustc_middle::ty::{
-    self, InferCtxtLike, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitableExt,
-};
-use rustc_span::DUMMY_SP;
-use rustc_type_ir::fold::TypeSuperFoldable;
+use rustc_macros::{HashStable_NoContext, TyDecodable, TyEncodable};
+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, TypeVisitor};
-use rustc_type_ir::{self as ir, CanonicalVarValues, Interner};
+use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
+use rustc_type_ir::{self as ty, CanonicalVarValues, Interner};
 use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Generic};
-use std::ops::ControlFlow;
-
-use crate::traits::coherence;
-
-use super::inspect::ProofTreeBuilder;
-use super::{search_graph, GoalEvaluationKind, FIXPOINT_STEP_LIMIT};
-use super::{search_graph::SearchGraph, Goal};
-use super::{GoalSource, SolverMode};
+use tracing::{instrument, trace};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeBuilder};
+use crate::solve::search_graph::SearchGraph;
+use crate::solve::{
+    search_graph, CanonicalInput, CanonicalResponse, Certainty, Goal, GoalEvaluationKind,
+    GoalSource, MaybeCause, NestedNormalizationGoals, NoSolution, PredefinedOpaquesData,
+    QueryResult, SolverMode, FIXPOINT_STEP_LIMIT,
+};
 
 pub(super) mod canonical;
 mod probe;
 
-pub struct EvalCtxt<'a, Infcx, I = <Infcx as InferCtxtLike>::Interner>
+pub struct EvalCtxt<'a, Infcx, I = <Infcx as SolverDelegate>::Interner>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     /// The inference context that backs (mostly) inference and placeholder terms
@@ -112,9 +101,9 @@ pub struct NestedGoals<I: Interner> {
     ///
     /// Forgetting to replace the RHS with a fresh inference variable when we evaluate
     /// this goal results in an ICE..
-    pub normalizes_to_goals: Vec<ir::solve::Goal<I, ir::NormalizesTo<I>>>,
+    pub normalizes_to_goals: Vec<Goal<I, ty::NormalizesTo<I>>>,
     /// The rest of the goals which have not yet processed or remain ambiguous.
-    pub goals: Vec<(GoalSource, ir::solve::Goal<I, I::Predicate>)>,
+    pub goals: Vec<(GoalSource, Goal<I, I::Predicate>)>,
 }
 
 impl<I: Interner> NestedGoals<I> {
@@ -127,14 +116,36 @@ impl<I: Interner> NestedGoals<I> {
     }
 }
 
-#[derive(PartialEq, Eq, Debug, Hash, HashStable, Clone, Copy)]
+#[derive(PartialEq, Eq, Debug, Hash, HashStable_NoContext, Clone, Copy)]
 pub enum GenerateProofTree {
     Yes,
     No,
 }
 
-#[extension(pub trait InferCtxtEvalExt<'tcx>)]
-impl<'tcx> InferCtxt<'tcx> {
+pub trait SolverDelegateEvalExt: SolverDelegate {
+    fn evaluate_root_goal(
+        &self,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<Self::Interner>>);
+
+    // FIXME: This is only exposed because we need to use it in `analyse.rs`
+    // which is not yet uplifted. Once that's done, we should remove this.
+    fn evaluate_root_goal_raw(
+        &self,
+        goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(NestedNormalizationGoals<Self::Interner>, bool, Certainty), NoSolution>,
+        Option<inspect::GoalEvaluation<Self::Interner>>,
+    );
+}
+
+impl<Infcx, I> SolverDelegateEvalExt for Infcx
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Evaluates a goal from **outside** of the trait solver.
     ///
     /// Using this while inside of the solver is wrong as it uses a new
@@ -142,17 +153,34 @@ impl<'tcx> InferCtxt<'tcx> {
     #[instrument(level = "debug", skip(self))]
     fn evaluate_root_goal(
         &self,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        goal: Goal<I, I::Predicate>,
         generate_proof_tree: GenerateProofTree,
-    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<TyCtxt<'tcx>>>)
-    {
+    ) -> (Result<(bool, Certainty), NoSolution>, Option<inspect::GoalEvaluation<I>>) {
         EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
             ecx.evaluate_goal(GoalEvaluationKind::Root, GoalSource::Misc, goal)
         })
     }
+
+    #[instrument(level = "debug", skip(self))]
+    fn evaluate_root_goal_raw(
+        &self,
+        goal: Goal<I, I::Predicate>,
+        generate_proof_tree: GenerateProofTree,
+    ) -> (
+        Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution>,
+        Option<inspect::GoalEvaluation<I>>,
+    ) {
+        EvalCtxt::enter_root(self, generate_proof_tree, |ecx| {
+            ecx.evaluate_goal_raw(GoalEvaluationKind::Root, GoalSource::Misc, goal)
+        })
+    }
 }
 
-impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
+impl<'a, Infcx, I> EvalCtxt<'a, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn solver_mode(&self) -> SolverMode {
         self.search_graph.solver_mode()
     }
@@ -163,14 +191,13 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
 
     /// Creates a root evaluation context and search graph. This should only be
     /// used from outside of any evaluation, and other methods should be preferred
-    /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]).
+    /// over using this manually (such as [`SolverDelegateEvalExt::evaluate_root_goal`]).
     pub(super) fn enter_root<R>(
-        infcx: &InferCtxt<'tcx>,
+        infcx: &Infcx,
         generate_proof_tree: GenerateProofTree,
-        f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> R,
-    ) -> (R, Option<inspect::GoalEvaluation<TyCtxt<'tcx>>>) {
-        let mode = if infcx.intercrate { SolverMode::Coherence } else { SolverMode::Normal };
-        let mut search_graph = search_graph::SearchGraph::new(mode);
+        f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> R,
+    ) -> (R, Option<inspect::GoalEvaluation<I>>) {
+        let mut search_graph = search_graph::SearchGraph::new(infcx.solver_mode());
 
         let mut ecx = EvalCtxt {
             infcx,
@@ -181,10 +208,10 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             // Only relevant when canonicalizing the response,
             // which we don't do within this evaluation context.
             predefined_opaques_in_body: infcx
-                .tcx
+                .interner()
                 .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()),
             max_input_universe: ty::UniverseIndex::ROOT,
-            variables: ty::List::empty(),
+            variables: Default::default(),
             var_values: CanonicalVarValues::dummy(),
             is_normalizes_to_goal: false,
             tainted: Ok(()),
@@ -210,21 +237,14 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     /// This function takes care of setting up the inference context, setting the anchor,
     /// and registering opaques from the canonicalized input.
     fn enter_canonical<R>(
-        tcx: TyCtxt<'tcx>,
-        search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
-        canonical_input: CanonicalInput<'tcx>,
-        canonical_goal_evaluation: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
-        f: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>, Goal<'tcx, ty::Predicate<'tcx>>) -> R,
+        tcx: I,
+        search_graph: &'a mut search_graph::SearchGraph<I>,
+        canonical_input: CanonicalInput<I>,
+        canonical_goal_evaluation: &mut ProofTreeBuilder<Infcx>,
+        f: impl FnOnce(&mut EvalCtxt<'_, Infcx>, Goal<I, I::Predicate>) -> R,
     ) -> R {
-        let intercrate = match search_graph.solver_mode() {
-            SolverMode::Normal => false,
-            SolverMode::Coherence => true,
-        };
-        let (ref infcx, input, var_values) = tcx
-            .infer_ctxt()
-            .intercrate(intercrate)
-            .with_next_trait_solver(true)
-            .build_with_canonical(DUMMY_SP, &canonical_input);
+        let (ref infcx, input, var_values) =
+            SolverDelegate::build_with_canonical(tcx, search_graph.solver_mode(), &canonical_input);
 
         let mut ecx = EvalCtxt {
             infcx,
@@ -240,8 +260,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         };
 
         for &(key, ty) in &input.predefined_opaques_in_body.opaque_types {
-            let hidden_ty = ty::OpaqueHiddenType { ty, span: DUMMY_SP };
-            ecx.infcx.inject_new_hidden_type_unchecked(key, hidden_ty);
+            ecx.infcx.inject_new_hidden_type_unchecked(key, ty);
         }
 
         if !ecx.nested_goals.is_empty() {
@@ -256,7 +275,8 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         // instead of taking them. This would cause an ICE here, since we have
         // assertions against dropping an `InferCtxt` without taking opaques.
         // FIXME: Once we remove support for the old impl we can remove this.
-        let _ = infcx.take_opaque_types();
+        // FIXME: Could we make `build_with_canonical` into `enter_with_canonical` and call this at the end?
+        infcx.reset_opaque_types();
 
         result
     }
@@ -268,15 +288,15 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     /// logic of the solver.
     ///
     /// Instead of calling this function directly, use either [EvalCtxt::evaluate_goal]
-    /// if you're inside of the solver or [InferCtxtEvalExt::evaluate_root_goal] if you're
+    /// if you're inside of the solver or [SolverDelegateEvalExt::evaluate_root_goal] if you're
     /// outside of it.
     #[instrument(level = "debug", skip(tcx, search_graph, goal_evaluation), ret)]
     fn evaluate_canonical_goal(
-        tcx: TyCtxt<'tcx>,
-        search_graph: &'a mut search_graph::SearchGraph<TyCtxt<'tcx>>,
-        canonical_input: CanonicalInput<'tcx>,
-        goal_evaluation: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        tcx: I,
+        search_graph: &'a mut search_graph::SearchGraph<I>,
+        canonical_input: CanonicalInput<I>,
+        goal_evaluation: &mut ProofTreeBuilder<Infcx>,
+    ) -> QueryResult<I> {
         let mut canonical_goal_evaluation =
             goal_evaluation.new_canonical_goal_evaluation(canonical_input);
 
@@ -315,7 +335,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         &mut self,
         goal_evaluation_kind: GoalEvaluationKind,
         source: GoalSource,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
+        goal: Goal<I, I::Predicate>,
     ) -> Result<(bool, Certainty), NoSolution> {
         let (normalization_nested_goals, has_changed, certainty) =
             self.evaluate_goal_raw(goal_evaluation_kind, source, goal)?;
@@ -336,8 +356,8 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         &mut self,
         goal_evaluation_kind: GoalEvaluationKind,
         _source: GoalSource,
-        goal: Goal<'tcx, ty::Predicate<'tcx>>,
-    ) -> Result<(NestedNormalizationGoals<TyCtxt<'tcx>>, bool, Certainty), NoSolution> {
+        goal: Goal<I, I::Predicate>,
+    ) -> Result<(NestedNormalizationGoals<I>, bool, Certainty), NoSolution> {
         let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
         let mut goal_evaluation =
             self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
@@ -377,10 +397,10 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
 
     fn instantiate_response_discarding_overflow(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        original_values: Vec<ty::GenericArg<'tcx>>,
-        response: CanonicalResponse<'tcx>,
-    ) -> (NestedNormalizationGoals<TyCtxt<'tcx>>, Certainty, bool) {
+        param_env: I::ParamEnv,
+        original_values: Vec<I::GenericArg>,
+        response: CanonicalResponse<I>,
+    ) -> (NestedNormalizationGoals<I>, Certainty, bool) {
         if let Certainty::Maybe(MaybeCause::Overflow { .. }) = response.value.certainty {
             return (NestedNormalizationGoals::empty(), response.value.certainty, false);
         }
@@ -393,7 +413,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         (normalization_nested_goals, certainty, has_changed)
     }
 
-    fn compute_goal(&mut self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> QueryResult<'tcx> {
+    fn compute_goal(&mut self, goal: Goal<I, I::Predicate>) -> QueryResult<I> {
         let Goal { param_env, predicate } = goal;
         let kind = predicate.kind();
         if let Some(kind) = kind.no_bound_vars() {
@@ -429,7 +449,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
                     self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct })
                 }
                 ty::PredicateKind::ConstEquate(_, _) => {
-                    bug!("ConstEquate should not be emitted when `-Znext-solver` is active")
+                    panic!("ConstEquate should not be emitted when `-Znext-solver` is active")
                 }
                 ty::PredicateKind::NormalizesTo(predicate) => {
                     self.compute_normalizes_to_goal(Goal { param_env, predicate })
@@ -565,21 +585,16 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 
     /// Record impl args in the proof tree for later access by `InspectCandidate`.
-    pub(crate) fn record_impl_args(&mut self, impl_args: ty::GenericArgsRef<'tcx>) {
+    pub(crate) fn record_impl_args(&mut self, impl_args: I::GenericArgs) {
         self.inspect.record_impl_args(self.infcx, self.max_input_universe, impl_args)
     }
-}
 
-impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn interner(&self) -> I {
         self.infcx.interner()
     }
 
     #[instrument(level = "trace", skip(self))]
-    pub(super) fn add_normalizes_to_goal(
-        &mut self,
-        mut goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
-    ) {
+    pub(super) fn add_normalizes_to_goal(&mut self, mut goal: Goal<I, ty::NormalizesTo<I>>) {
         goal.predicate = goal
             .predicate
             .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
@@ -588,11 +603,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     }
 
     #[instrument(level = "debug", skip(self))]
-    pub(super) fn add_goal(
-        &mut self,
-        source: GoalSource,
-        mut goal: ir::solve::Goal<I, I::Predicate>,
-    ) {
+    pub(super) fn add_goal(&mut self, source: GoalSource, mut goal: Goal<I, I::Predicate>) {
         goal.predicate = goal
             .predicate
             .fold_with(&mut ReplaceAliasWithInfer { ecx: self, param_env: goal.param_env });
@@ -604,7 +615,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn add_goals(
         &mut self,
         source: GoalSource,
-        goals: impl IntoIterator<Item = ir::solve::Goal<I, I::Predicate>>,
+        goals: impl IntoIterator<Item = Goal<I, I::Predicate>>,
     ) {
         for goal in goals {
             self.add_goal(source, goal);
@@ -627,8 +638,8 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     /// If `kind` is an integer inference variable this will still return a ty infer var.
     pub(super) fn next_term_infer_of_kind(&mut self, kind: I::Term) -> I::Term {
         match kind.kind() {
-            ir::TermKind::Ty(_) => self.next_ty_infer().into(),
-            ir::TermKind::Const(_) => self.next_const_infer().into(),
+            ty::TermKind::Ty(_) => self.next_ty_infer().into(),
+            ty::TermKind::Const(_) => self.next_const_infer().into(),
         }
     }
 
@@ -637,20 +648,17 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     /// This is the case if the `term` does not occur in any other part of the predicate
     /// and is able to name all other placeholder and inference variables.
     #[instrument(level = "trace", skip(self), ret)]
-    pub(super) fn term_is_fully_unconstrained(
-        &self,
-        goal: ir::solve::Goal<I, ir::NormalizesTo<I>>,
-    ) -> bool {
+    pub(super) fn term_is_fully_unconstrained(&self, goal: Goal<I, ty::NormalizesTo<I>>) -> bool {
         let universe_of_term = match goal.predicate.term.kind() {
-            ir::TermKind::Ty(ty) => {
-                if let ir::Infer(ir::TyVar(vid)) = ty.kind() {
+            ty::TermKind::Ty(ty) => {
+                if let ty::Infer(ty::TyVar(vid)) = ty.kind() {
                     self.infcx.universe_of_ty(vid).unwrap()
                 } else {
                     return false;
                 }
             }
-            ir::TermKind::Const(ct) => {
-                if let ir::ConstKind::Infer(ir::InferConst::Var(vid)) = ct.kind() {
+            ty::TermKind::Const(ct) => {
+                if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() {
                     self.infcx.universe_of_ct(vid).unwrap()
                 } else {
                     return false;
@@ -658,14 +666,14 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             }
         };
 
-        struct ContainsTermOrNotNameable<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
+        struct ContainsTermOrNotNameable<'a, Infcx: SolverDelegate<Interner = I>, I: Interner> {
             term: I::Term,
-            universe_of_term: ir::UniverseIndex,
+            universe_of_term: ty::UniverseIndex,
             infcx: &'a Infcx,
         }
 
-        impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> {
-            fn check_nameable(&self, universe: ir::UniverseIndex) -> ControlFlow<()> {
+        impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ContainsTermOrNotNameable<'_, Infcx, I> {
+            fn check_nameable(&self, universe: ty::UniverseIndex) -> ControlFlow<()> {
                 if self.universe_of_term.can_name(universe) {
                     ControlFlow::Continue(())
                 } else {
@@ -674,15 +682,15 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             }
         }
 
-        impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeVisitor<I>
+        impl<Infcx: SolverDelegate<Interner = I>, I: Interner> TypeVisitor<I>
             for ContainsTermOrNotNameable<'_, Infcx, I>
         {
             type Result = ControlFlow<()>;
             fn visit_ty(&mut self, t: I::Ty) -> Self::Result {
                 match t.kind() {
-                    ir::Infer(ir::TyVar(vid)) => {
-                        if let ir::TermKind::Ty(term) = self.term.kind()
-                            && let ir::Infer(ir::TyVar(term_vid)) = term.kind()
+                    ty::Infer(ty::TyVar(vid)) => {
+                        if let ty::TermKind::Ty(term) = self.term.kind()
+                            && let ty::Infer(ty::TyVar(term_vid)) = term.kind()
                             && self.infcx.root_ty_var(vid) == self.infcx.root_ty_var(term_vid)
                         {
                             ControlFlow::Break(())
@@ -690,7 +698,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
                             self.check_nameable(self.infcx.universe_of_ty(vid).unwrap())
                         }
                     }
-                    ir::Placeholder(p) => self.check_nameable(p.universe()),
+                    ty::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if t.has_non_region_infer() || t.has_placeholders() {
                             t.super_visit_with(self)
@@ -703,9 +711,9 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
 
             fn visit_const(&mut self, c: I::Const) -> Self::Result {
                 match c.kind() {
-                    ir::ConstKind::Infer(ir::InferConst::Var(vid)) => {
-                        if let ir::TermKind::Const(term) = self.term.kind()
-                            && let ir::ConstKind::Infer(ir::InferConst::Var(term_vid)) = term.kind()
+                    ty::ConstKind::Infer(ty::InferConst::Var(vid)) => {
+                        if let ty::TermKind::Const(term) = self.term.kind()
+                            && let ty::ConstKind::Infer(ty::InferConst::Var(term_vid)) = term.kind()
                             && self.infcx.root_const_var(vid) == self.infcx.root_const_var(term_vid)
                         {
                             ControlFlow::Break(())
@@ -713,7 +721,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
                             self.check_nameable(self.infcx.universe_of_ct(vid).unwrap())
                         }
                     }
-                    ir::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
+                    ty::ConstKind::Placeholder(p) => self.check_nameable(p.universe()),
                     _ => {
                         if c.has_non_region_infer() || c.has_placeholders() {
                             c.super_visit_with(self)
@@ -741,7 +749,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         lhs: T,
         rhs: T,
     ) -> Result<(), NoSolution> {
-        self.relate(param_env, lhs, ir::Variance::Invariant, rhs)
+        self.relate(param_env, lhs, ty::Variance::Invariant, rhs)
     }
 
     /// This should be used when relating a rigid alias with another type.
@@ -753,8 +761,8 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
     pub(super) fn relate_rigid_alias_non_alias(
         &mut self,
         param_env: I::ParamEnv,
-        alias: ir::AliasTerm<I>,
-        variance: ir::Variance,
+        alias: ty::AliasTerm<I>,
+        variance: ty::Variance,
         term: I::Term,
     ) -> Result<(), NoSolution> {
         // NOTE: this check is purely an optimization, the structural eq would
@@ -770,7 +778,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
             // Alternatively we could modify `Equate` for this case by adding another
             // variant to `StructurallyRelateAliases`.
             let identity_args = self.fresh_args_for_item(alias.def_id);
-            let rigid_ctor = ir::AliasTerm::new(tcx, alias.def_id, identity_args);
+            let rigid_ctor = ty::AliasTerm::new(tcx, alias.def_id, identity_args);
             let ctor_term = rigid_ctor.to_term(tcx);
             let obligations =
                 self.infcx.eq_structurally_relating_aliases(param_env, term, ctor_term)?;
@@ -803,7 +811,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         sub: T,
         sup: T,
     ) -> Result<(), NoSolution> {
-        self.relate(param_env, sub, ir::Variance::Covariant, sup)
+        self.relate(param_env, sub, ty::Variance::Covariant, sup)
     }
 
     #[instrument(level = "trace", skip(self, param_env), ret)]
@@ -811,7 +819,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         &mut self,
         param_env: I::ParamEnv,
         lhs: T,
-        variance: ir::Variance,
+        variance: ty::Variance,
         rhs: T,
     ) -> Result<(), NoSolution> {
         let goals = self.infcx.relate(param_env, lhs, variance, rhs)?;
@@ -830,20 +838,20 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         param_env: I::ParamEnv,
         lhs: T,
         rhs: T,
-    ) -> Result<Vec<ir::solve::Goal<I, I::Predicate>>, NoSolution> {
-        self.infcx.relate(param_env, lhs, ir::Variance::Invariant, rhs)
+    ) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {
+        self.infcx.relate(param_env, lhs, ty::Variance::Invariant, rhs)
     }
 
     pub(super) fn instantiate_binder_with_infer<T: TypeFoldable<I> + Copy>(
         &self,
-        value: ir::Binder<I, T>,
+        value: ty::Binder<I, T>,
     ) -> T {
         self.infcx.instantiate_binder_with_infer(value)
     }
 
     pub(super) fn enter_forall<T: TypeFoldable<I> + Copy, U>(
         &self,
-        value: ir::Binder<I, T>,
+        value: ty::Binder<I, T>,
         f: impl FnOnce(T) -> U,
     ) -> U {
         self.infcx.enter_forall(value, f)
@@ -863,89 +871,72 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> EvalCtxt<'_, Infcx> {
         }
         args
     }
-}
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
-    pub(super) fn register_ty_outlives(&self, ty: Ty<'tcx>, lt: ty::Region<'tcx>) {
-        self.infcx.register_region_obligation_with_cause(ty, lt, &ObligationCause::dummy());
+    pub(super) fn register_ty_outlives(&self, ty: I::Ty, lt: I::Region) {
+        self.infcx.register_ty_outlives(ty, lt);
     }
 
-    pub(super) fn register_region_outlives(&self, a: ty::Region<'tcx>, b: ty::Region<'tcx>) {
+    pub(super) fn register_region_outlives(&self, a: I::Region, b: I::Region) {
         // `b : a` ==> `a <= b`
-        // (inlined from `InferCtxt::region_outlives_predicate`)
-        self.infcx.sub_regions(
-            rustc_infer::infer::SubregionOrigin::RelateRegionParamBound(DUMMY_SP),
-            b,
-            a,
-        );
+        self.infcx.sub_regions(b, a);
     }
 
     /// Computes the list of goals required for `arg` to be well-formed
     pub(super) fn well_formed_goals(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        arg: ty::GenericArg<'tcx>,
-    ) -> Option<impl Iterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>> {
-        crate::traits::wf::unnormalized_obligations(self.infcx, param_env, arg)
-            .map(|obligations| obligations.into_iter().map(|obligation| obligation.into()))
-    }
-
-    pub(super) fn is_transmutable(
-        &self,
-        src_and_dst: rustc_transmute::Types<'tcx>,
-        assume: rustc_transmute::Assume,
-    ) -> Result<Certainty, NoSolution> {
-        use rustc_transmute::Answer;
-        // FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
-        match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
-            ObligationCause::dummy(),
-            src_and_dst,
-            assume,
-        ) {
-            Answer::Yes => Ok(Certainty::Yes),
-            Answer::No(_) | Answer::If(_) => Err(NoSolution),
-        }
+        param_env: I::ParamEnv,
+        arg: I::GenericArg,
+    ) -> Option<Vec<Goal<I, I::Predicate>>> {
+        self.infcx.well_formed_goals(param_env, arg)
     }
 
     pub(super) fn trait_ref_is_knowable(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
+        param_env: I::ParamEnv,
+        trait_ref: ty::TraitRef<I>,
     ) -> Result<bool, NoSolution> {
         let infcx = self.infcx;
         let lazily_normalize_ty = |ty| self.structurally_normalize_ty(param_env, ty);
-        coherence::trait_ref_is_knowable(infcx, trait_ref, lazily_normalize_ty)
-            .map(|is_knowable| is_knowable.is_ok())
+        infcx.trait_ref_is_knowable(trait_ref, lazily_normalize_ty)
     }
 
-    pub(super) fn can_define_opaque_ty(&self, def_id: impl Into<DefId>) -> bool {
-        self.infcx.can_define_opaque_ty(def_id)
+    pub(super) fn fetch_eligible_assoc_item(
+        &self,
+        param_env: I::ParamEnv,
+        goal_trait_ref: ty::TraitRef<I>,
+        trait_assoc_def_id: I::DefId,
+        impl_def_id: I::DefId,
+    ) -> Result<Option<I::DefId>, NoSolution> {
+        self.infcx.fetch_eligible_assoc_item(
+            param_env,
+            goal_trait_ref,
+            trait_assoc_def_id,
+            impl_def_id,
+        )
+    }
+
+    pub(super) fn can_define_opaque_ty(&self, def_id: I::LocalDefId) -> bool {
+        self.infcx.defining_opaque_types().contains(&def_id)
     }
 
     pub(super) fn insert_hidden_type(
         &mut self,
-        opaque_type_key: OpaqueTypeKey<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        hidden_ty: Ty<'tcx>,
+        opaque_type_key: ty::OpaqueTypeKey<I>,
+        param_env: I::ParamEnv,
+        hidden_ty: I::Ty,
     ) -> Result<(), NoSolution> {
         let mut goals = Vec::new();
-        self.infcx.insert_hidden_type(
-            opaque_type_key,
-            DUMMY_SP,
-            param_env,
-            hidden_ty,
-            &mut goals,
-        )?;
+        self.infcx.insert_hidden_type(opaque_type_key, param_env, hidden_ty, &mut goals)?;
         self.add_goals(GoalSource::Misc, goals);
         Ok(())
     }
 
     pub(super) fn add_item_bounds_for_hidden_type(
         &mut self,
-        opaque_def_id: DefId,
-        opaque_args: ty::GenericArgsRef<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        hidden_ty: Ty<'tcx>,
+        opaque_def_id: I::DefId,
+        opaque_args: I::GenericArgs,
+        param_env: I::ParamEnv,
+        hidden_ty: I::Ty,
     ) {
         let mut goals = Vec::new();
         self.infcx.add_item_bounds_for_hidden_type(
@@ -962,10 +953,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     // current inference context.
     pub(super) fn unify_existing_opaque_tys(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        key: ty::OpaqueTypeKey<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Vec<CanonicalResponse<'tcx>> {
+        param_env: I::ParamEnv,
+        key: ty::OpaqueTypeKey<I>,
+        ty: I::Ty,
+    ) -> Vec<CanonicalResponse<I>> {
         // FIXME: Super inefficient to be cloning this...
         let opaques = self.infcx.clone_opaque_types_for_query_response();
 
@@ -984,7 +975,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     }
                     ecx.eq(param_env, candidate_ty, ty)?;
                     ecx.add_item_bounds_for_hidden_type(
-                        candidate_key.def_id.to_def_id(),
+                        candidate_key.def_id.into(),
                         candidate_key.args,
                         param_env,
                         candidate_ty,
@@ -1001,23 +992,20 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     // as an ambiguity rather than no-solution.
     pub(super) fn try_const_eval_resolve(
         &self,
-        param_env: ty::ParamEnv<'tcx>,
-        unevaluated: ty::UnevaluatedConst<'tcx>,
-    ) -> Option<ty::Const<'tcx>> {
-        use rustc_middle::mir::interpret::ErrorHandled;
-        match self.infcx.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
-            Ok(Some(val)) => Some(ty::Const::new_value(
-                self.interner(),
-                val,
-                self.interner()
-                    .type_of(unevaluated.def)
-                    .instantiate(self.interner(), unevaluated.args),
-            )),
-            Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
-            Err(ErrorHandled::Reported(e, _)) => {
-                Some(ty::Const::new_error(self.interner(), e.into()))
-            }
-        }
+        param_env: I::ParamEnv,
+        unevaluated: ty::UnevaluatedConst<I>,
+    ) -> Option<I::Const> {
+        self.infcx.try_const_eval_resolve(param_env, unevaluated)
+    }
+
+    pub(super) fn is_transmutable(
+        &mut self,
+        param_env: I::ParamEnv,
+        dst: I::Ty,
+        src: I::Ty,
+        assume: I::Const,
+    ) -> Result<Certainty, NoSolution> {
+        self.infcx.is_transmutable(param_env, dst, src, assume)
     }
 }
 
@@ -1030,7 +1018,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 /// solving. See tests/ui/traits/next-solver/cycles/cycle-modulo-ambig-aliases.rs.
 struct ReplaceAliasWithInfer<'me, 'a, Infcx, I>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     ecx: &'me mut EvalCtxt<'a, Infcx>,
@@ -1039,7 +1027,7 @@ where
 
 impl<Infcx, I> TypeFolder<I> for ReplaceAliasWithInfer<'_, '_, Infcx, I>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     fn interner(&self) -> I {
@@ -1048,16 +1036,16 @@ where
 
     fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {
         match ty.kind() {
-            ir::Alias(..) if !ty.has_escaping_bound_vars() => {
+            ty::Alias(..) if !ty.has_escaping_bound_vars() => {
                 let infer_ty = self.ecx.next_ty_infer();
-                let normalizes_to = ir::PredicateKind::AliasRelate(
+                let normalizes_to = ty::PredicateKind::AliasRelate(
                     ty.into(),
                     infer_ty.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ty
             }
@@ -1067,16 +1055,16 @@ where
 
     fn fold_const(&mut self, ct: I::Const) -> I::Const {
         match ct.kind() {
-            ir::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
+            ty::ConstKind::Unevaluated(..) if !ct.has_escaping_bound_vars() => {
                 let infer_ct = self.ecx.next_const_infer();
-                let normalizes_to = ir::PredicateKind::AliasRelate(
+                let normalizes_to = ty::PredicateKind::AliasRelate(
                     ct.into(),
                     infer_ct.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 );
                 self.ecx.add_goal(
                     GoalSource::Misc,
-                    ir::solve::Goal::new(self.interner(), self.param_env, normalizes_to),
+                    Goal::new(self.interner(), self.param_env, normalizes_to),
                 );
                 infer_ct
             }
diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
index ad6fdd2707d..1c5358b3edb 100644
--- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/probe.rs
@@ -1,15 +1,16 @@
-use crate::solve::assembly::Candidate;
-
-use super::EvalCtxt;
-use rustc_next_trait_solver::solve::{
-    inspect, BuiltinImplSource, CandidateSource, NoSolution, QueryResult,
-};
-use rustc_type_ir::{InferCtxtLike, Interner};
 use std::marker::PhantomData;
 
+use rustc_type_ir::Interner;
+use tracing::instrument;
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::Candidate;
+use crate::solve::inspect;
+use crate::solve::{BuiltinImplSource, CandidateSource, EvalCtxt, NoSolution, QueryResult};
+
 pub(in crate::solve) struct ProbeCtxt<'me, 'a, Infcx, I, F, T>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     ecx: &'me mut EvalCtxt<'a, Infcx, I>,
@@ -20,7 +21,7 @@ where
 impl<Infcx, I, F, T> ProbeCtxt<'_, '_, Infcx, I, F, T>
 where
     F: FnOnce(&T) -> inspect::ProbeKind<I>,
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     pub(in crate::solve) fn enter(self, f: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> T) -> T {
@@ -56,7 +57,7 @@ where
 
 pub(in crate::solve) struct TraitProbeCtxt<'me, 'a, Infcx, I, F>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     cx: ProbeCtxt<'me, 'a, Infcx, I, F, QueryResult<I>>,
@@ -65,7 +66,7 @@ where
 
 impl<Infcx, I, F> TraitProbeCtxt<'_, '_, Infcx, I, F>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
     F: FnOnce(&QueryResult<I>) -> inspect::ProbeKind<I>,
 {
@@ -80,7 +81,7 @@ where
 
 impl<'a, Infcx, I> EvalCtxt<'a, Infcx, I>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     /// `probe_kind` is only called when proof tree building is enabled so it can be
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/build.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
index 35750598bc7..5fbec4b28d4 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/build.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/build.rs
@@ -3,16 +3,19 @@
 //! This code is *a bit* of a mess and can hopefully be
 //! mostly ignored. For a general overview of how it works,
 //! see the comment on [ProofTreeBuilder].
+
 use std::marker::PhantomData;
 use std::mem;
 
+use rustc_type_ir::{self as ty, Interner};
+
+use crate::infcx::SolverDelegate;
 use crate::solve::eval_ctxt::canonical;
-use crate::solve::{self, inspect, GenerateProofTree};
-use rustc_middle::bug;
-use rustc_next_trait_solver::solve::{
-    CanonicalInput, Certainty, Goal, GoalSource, QueryInput, QueryResult,
+use crate::solve::inspect;
+use crate::solve::{
+    CanonicalInput, Certainty, GenerateProofTree, Goal, GoalEvaluationKind, GoalSource, QueryInput,
+    QueryResult,
 };
-use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 
 /// The core data structure when building proof trees.
 ///
@@ -34,9 +37,9 @@ use rustc_type_ir::{self as ty, InferCtxtLike, Interner};
 /// trees. At the end of trait solving `ProofTreeBuilder::finalize`
 /// is called to recursively convert the whole structure to a
 /// finished proof tree.
-pub(in crate::solve) struct ProofTreeBuilder<Infcx, I = <Infcx as InferCtxtLike>::Interner>
+pub(in crate::solve) struct ProofTreeBuilder<Infcx, I = <Infcx as SolverDelegate>::Interner>
 where
-    Infcx: InferCtxtLike<Interner = I>,
+    Infcx: SolverDelegate<Interner = I>,
     I: Interner,
 {
     _infcx: PhantomData<Infcx>,
@@ -170,7 +173,7 @@ impl<I: Interner> WipCanonicalGoalEvaluationStep<I> {
         for _ in 0..self.probe_depth {
             match current.steps.last_mut() {
                 Some(WipProbeStep::NestedProbe(p)) => current = p,
-                _ => bug!(),
+                _ => panic!(),
             }
         }
         current
@@ -232,7 +235,7 @@ impl<I: Interner> WipProbeStep<I> {
     }
 }
 
-impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
+impl<Infcx: SolverDelegate<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
     fn new(state: impl Into<DebugSolver<I>>) -> ProofTreeBuilder<Infcx> {
         ProofTreeBuilder { state: Some(Box::new(state.into())), _infcx: PhantomData }
     }
@@ -293,15 +296,15 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
         &mut self,
         goal: Goal<I, I::Predicate>,
         orig_values: &[I::GenericArg],
-        kind: solve::GoalEvaluationKind,
+        kind: GoalEvaluationKind,
     ) -> ProofTreeBuilder<Infcx> {
         self.opt_nested(|| match kind {
-            solve::GoalEvaluationKind::Root => Some(WipGoalEvaluation {
+            GoalEvaluationKind::Root => Some(WipGoalEvaluation {
                 uncanonicalized_goal: goal,
                 orig_values: orig_values.to_vec(),
                 evaluation: None,
             }),
-            solve::GoalEvaluationKind::Nested => None,
+            GoalEvaluationKind::Nested => None,
         })
     }
 
@@ -413,7 +416,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
             Some(DebugSolver::CanonicalGoalEvaluationStep(state)) => {
                 state.var_values.push(arg.into());
             }
-            Some(s) => bug!("tried to add var values to {s:?}"),
+            Some(s) => panic!("tried to add var values to {s:?}"),
         }
     }
 
@@ -430,7 +433,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 }));
                 state.probe_depth += 1;
             }
-            Some(s) => bug!("tried to start probe to {s:?}"),
+            Some(s) => panic!("tried to start probe to {s:?}"),
         }
     }
 
@@ -441,7 +444,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 let prev = state.current_evaluation_scope().kind.replace(probe_kind);
                 assert_eq!(prev, None);
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -458,7 +461,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 let prev = state.current_evaluation_scope().final_state.replace(final_state);
                 assert_eq!(prev, None);
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -494,7 +497,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 );
                 state.current_evaluation_scope().steps.push(WipProbeStep::AddGoal(source, goal))
             }
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -518,7 +521,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                     .push(WipProbeStep::RecordImplArgs { impl_args });
             }
             None => {}
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -531,7 +534,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                     .push(WipProbeStep::MakeCanonicalResponse { shallow_certainty });
             }
             None => {}
-            _ => bug!(),
+            _ => panic!(),
         }
     }
 
@@ -544,7 +547,7 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> ProofTreeBuilder<Infcx> {
                 state.var_values.truncate(num_var_values);
                 state.probe_depth -= 1;
             }
-            _ => bug!(),
+            _ => panic!(),
         }
 
         self
diff --git a/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs
new file mode 100644
index 00000000000..0d8c0060126
--- /dev/null
+++ b/compiler/rustc_next_trait_solver/src/solve/inspect/mod.rs
@@ -0,0 +1,6 @@
+pub use rustc_type_ir::solve::inspect::*;
+
+mod build;
+pub(in crate::solve) use build::*;
+
+pub use crate::solve::eval_ctxt::canonical::instantiate_canonical_state;
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 4f1be5cbc85..02069016c2b 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -13,38 +13,23 @@
 //!
 //! FIXME(@lcnr): Write that section. If you read this before then ask me
 //! about it on zulip.
-use rustc_hir::def_id::DefId;
-use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::query::NoSolution;
-use rustc_macros::extension;
-use rustc_middle::bug;
-use rustc_middle::infer::canonical::CanonicalVarInfos;
-use rustc_middle::traits::solve::{
-    CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, QueryResult, Response,
-};
-use rustc_middle::ty::{
-    self, AliasRelationDirection, CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, Ty,
-    TyCtxt, TypeOutlivesPredicate, UniverseIndex,
-};
 
 mod alias_relate;
 mod assembly;
 mod eval_ctxt;
-mod fulfill;
 pub mod inspect;
-mod normalize;
 mod normalizes_to;
 mod project_goals;
 mod search_graph;
-mod select;
 mod trait_goals;
 
-pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt};
-pub use fulfill::{FulfillmentCtxt, NextSolverError};
-pub(crate) use normalize::deeply_normalize_for_diagnostics;
-pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
-pub use select::InferCtxtSelectExt;
+use rustc_type_ir::inherent::*;
+pub use rustc_type_ir::solve::*;
+use rustc_type_ir::{self as ty, Interner};
+use tracing::instrument;
+
+pub use self::eval_ctxt::{EvalCtxt, GenerateProofTree, SolverDelegateEvalExt};
+use crate::infcx::SolverDelegate;
 
 /// How many fixpoint iterations we should attempt inside of the solver before bailing
 /// with overflow.
@@ -57,40 +42,30 @@ pub use select::InferCtxtSelectExt;
 /// recursion limit again. However, this feels very unlikely.
 const FIXPOINT_STEP_LIMIT: usize = 8;
 
-#[derive(Debug, Clone, Copy)]
-enum SolverMode {
-    /// Ordinary trait solving, using everywhere except for coherence.
-    Normal,
-    /// Trait solving during coherence. There are a few notable differences
-    /// between coherence and ordinary trait solving.
-    ///
-    /// Most importantly, trait solving during coherence must not be incomplete,
-    /// i.e. return `Err(NoSolution)` for goals for which a solution exists.
-    /// This means that we must not make any guesses or arbitrary choices.
-    Coherence,
-}
-
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 enum GoalEvaluationKind {
     Root,
     Nested,
 }
 
-#[extension(trait CanonicalResponseExt)]
-impl<'tcx> Canonical<'tcx, Response<TyCtxt<'tcx>>> {
-    fn has_no_inference_or_external_constraints(&self) -> bool {
-        self.value.external_constraints.region_constraints.is_empty()
-            && self.value.var_values.is_identity()
-            && self.value.external_constraints.opaque_types.is_empty()
-    }
+fn has_no_inference_or_external_constraints<I: Interner>(
+    response: ty::Canonical<I, Response<I>>,
+) -> bool {
+    response.value.external_constraints.region_constraints.is_empty()
+        && response.value.var_values.is_identity()
+        && response.value.external_constraints.opaque_types.is_empty()
 }
 
-impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
+impl<'a, Infcx, I> EvalCtxt<'a, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self))]
     fn compute_type_outlives_goal(
         &mut self,
-        goal: Goal<'tcx, TypeOutlivesPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::OutlivesPredicate<I, I::Ty>>,
+    ) -> QueryResult<I> {
         let ty::OutlivesPredicate(ty, lt) = goal.predicate;
         self.register_ty_outlives(ty, lt);
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -99,21 +74,18 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     fn compute_region_outlives_goal(
         &mut self,
-        goal: Goal<'tcx, RegionOutlivesPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::OutlivesPredicate<I, I::Region>>,
+    ) -> QueryResult<I> {
         let ty::OutlivesPredicate(a, b) = goal.predicate;
         self.register_region_outlives(a, b);
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn compute_coerce_goal(
-        &mut self,
-        goal: Goal<'tcx, CoercePredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_coerce_goal(&mut self, goal: Goal<I, ty::CoercePredicate<I>>) -> QueryResult<I> {
         self.compute_subtype_goal(Goal {
             param_env: goal.param_env,
-            predicate: SubtypePredicate {
+            predicate: ty::SubtypePredicate {
                 a_is_expected: false,
                 a: goal.predicate.a,
                 b: goal.predicate.b,
@@ -122,10 +94,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn compute_subtype_goal(
-        &mut self,
-        goal: Goal<'tcx, SubtypePredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_subtype_goal(&mut self, goal: Goal<I, ty::SubtypePredicate<I>>) -> QueryResult<I> {
         if goal.predicate.a.is_ty_var() && goal.predicate.b.is_ty_var() {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
         } else {
@@ -134,8 +103,8 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
         }
     }
 
-    fn compute_object_safe_goal(&mut self, trait_def_id: DefId) -> QueryResult<'tcx> {
-        if self.interner().is_object_safe(trait_def_id) {
+    fn compute_object_safe_goal(&mut self, trait_def_id: I::DefId) -> QueryResult<I> {
+        if self.interner().trait_is_object_safe(trait_def_id) {
             self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         } else {
             Err(NoSolution)
@@ -143,10 +112,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 
     #[instrument(level = "trace", skip(self))]
-    fn compute_well_formed_goal(
-        &mut self,
-        goal: Goal<'tcx, ty::GenericArg<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn compute_well_formed_goal(&mut self, goal: Goal<I, I::GenericArg>) -> QueryResult<I> {
         match self.well_formed_goals(goal.param_env, goal.predicate) {
             Some(goals) => {
                 self.add_goals(GoalSource::Misc, goals);
@@ -159,8 +125,8 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     fn compute_const_evaluatable_goal(
         &mut self,
-        Goal { param_env, predicate: ct }: Goal<'tcx, ty::Const<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        Goal { param_env, predicate: ct }: Goal<I, I::Const>,
+    ) -> QueryResult<I> {
         match ct.kind() {
             ty::ConstKind::Unevaluated(uv) => {
                 // We never return `NoSolution` here as `try_const_eval_resolve` emits an
@@ -190,7 +156,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             // - `Bound` cannot exist as we don't have a binder around the self Type
             // - `Expr` is part of `feature(generic_const_exprs)` and is not implemented yet
             ty::ConstKind::Param(_) | ty::ConstKind::Bound(_, _) | ty::ConstKind::Expr(_) => {
-                bug!("unexpect const kind: {:?}", ct)
+                panic!("unexpect const kind: {:?}", ct)
             }
         }
     }
@@ -198,8 +164,8 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self), ret)]
     fn compute_const_arg_has_type_goal(
         &mut self,
-        goal: Goal<'tcx, (ty::Const<'tcx>, Ty<'tcx>)>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, (I::Const, I::Ty)>,
+    ) -> QueryResult<I> {
         let (ct, ty) = goal.predicate;
 
         let ct_ty = match ct.kind() {
@@ -216,7 +182,7 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
                 return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
             }
             ty::ConstKind::Unevaluated(uv) => {
-                self.interner().type_of(uv.def).instantiate(self.interner(), uv.args)
+                self.interner().type_of(uv.def).instantiate(self.interner(), &uv.args)
             }
             ty::ConstKind::Expr(_) => unimplemented!(
                 "`feature(generic_const_exprs)` is not supported in the new trait solver"
@@ -224,10 +190,10 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
             ty::ConstKind::Param(_) => {
                 unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
             }
-            ty::ConstKind::Bound(_, _) => bug!("escaping bound vars in {:?}", ct),
+            ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
             ty::ConstKind::Value(ty, _) => ty,
             ty::ConstKind::Placeholder(placeholder) => {
-                placeholder.find_const_ty_from_env(goal.param_env)
+                self.interner().find_const_ty_from_env(goal.param_env, placeholder)
             }
         };
 
@@ -236,15 +202,19 @@ impl<'a, 'tcx> EvalCtxt<'a, InferCtxt<'tcx>> {
     }
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Try to merge multiple possible ways to prove a goal, if that is not possible returns `None`.
     ///
     /// In this case we tend to flounder and return ambiguity by calling `[EvalCtxt::flounder]`.
     #[instrument(level = "trace", skip(self), ret)]
     fn try_merge_responses(
         &mut self,
-        responses: &[CanonicalResponse<'tcx>],
-    ) -> Option<CanonicalResponse<'tcx>> {
+        responses: &[CanonicalResponse<I>],
+    ) -> Option<CanonicalResponse<I>> {
         if responses.is_empty() {
             return None;
         }
@@ -260,14 +230,14 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             .iter()
             .find(|response| {
                 response.value.certainty == Certainty::Yes
-                    && response.has_no_inference_or_external_constraints()
+                    && has_no_inference_or_external_constraints(**response)
             })
             .copied()
     }
 
     /// If we fail to merge responses we flounder and return overflow or ambiguity.
     #[instrument(level = "trace", skip(self), ret)]
-    fn flounder(&mut self, responses: &[CanonicalResponse<'tcx>]) -> QueryResult<'tcx> {
+    fn flounder(&mut self, responses: &[CanonicalResponse<I>]) -> QueryResult<I> {
         if responses.is_empty() {
             return Err(NoSolution);
         }
@@ -277,7 +247,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 certainty.unify_with(response.value.certainty)
             })
         else {
-            bug!("expected flounder response to be ambiguous")
+            panic!("expected flounder response to be ambiguous")
         };
 
         Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
@@ -291,9 +261,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self, param_env), ret)]
     fn structurally_normalize_ty(
         &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Result<Ty<'tcx>, NoSolution> {
+        param_env: I::ParamEnv,
+        ty: I::Ty,
+    ) -> Result<I::Ty, NoSolution> {
         if let ty::Alias(..) = ty.kind() {
             let normalized_ty = self.next_ty_infer();
             let alias_relate_goal = Goal::new(
@@ -302,7 +272,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 ty::PredicateKind::AliasRelate(
                     ty.into(),
                     normalized_ty.into(),
-                    AliasRelationDirection::Equate,
+                    ty::AliasRelationDirection::Equate,
                 ),
             );
             self.add_goal(GoalSource::Misc, alias_relate_goal);
@@ -314,17 +284,17 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     }
 }
 
-fn response_no_constraints_raw<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    max_universe: UniverseIndex,
-    variables: CanonicalVarInfos<'tcx>,
+fn response_no_constraints_raw<I: Interner>(
+    tcx: I,
+    max_universe: ty::UniverseIndex,
+    variables: I::CanonicalVars,
     certainty: Certainty,
-) -> CanonicalResponse<'tcx> {
-    Canonical {
+) -> CanonicalResponse<I> {
+    ty::Canonical {
         max_universe,
         variables,
         value: Response {
-            var_values: CanonicalVarValues::make_identity(tcx, variables),
+            var_values: ty::CanonicalVarValues::make_identity(tcx, variables),
             // FIXME: maybe we should store the "no response" version in tcx, like
             // we do for tcx.types and stuff.
             external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs
index a6e4b6ff4a8..0f1c1f13c16 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs
@@ -1,14 +1,19 @@
-use crate::solve::EvalCtxt;
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty;
+use rustc_type_ir::{self as ty, Interner};
+use tracing::instrument;
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn normalize_anon_const(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         if let Some(normalized_const) = self.try_const_eval_resolve(
             goal.param_env,
             ty::UnevaluatedConst::new(goal.predicate.alias.def_id, goal.predicate.alias.args),
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
index 41b2b9cd4d2..8436f3ad484 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/inherent.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs
@@ -4,17 +4,21 @@
 //! 1. instantiate generic parameters,
 //! 2. equate the self type, and
 //! 3. instantiate and register where clauses.
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
-use rustc_middle::ty;
 
-use crate::solve::EvalCtxt;
+use rustc_type_ir::{self as ty, Interner};
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_inherent_associated_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let inherent = goal.predicate.alias.expect_ty(tcx);
 
@@ -25,7 +29,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         self.eq(
             goal.param_env,
             inherent.self_ty(),
-            tcx.type_of(impl_def_id).instantiate(tcx, impl_args),
+            tcx.type_of(impl_def_id).instantiate(tcx, &impl_args),
         )?;
 
         // Equate IAT with the RHS of the project goal
@@ -40,12 +44,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         self.add_goals(
             GoalSource::Misc,
             tcx.predicates_of(inherent.def_id)
-                .instantiate(tcx, inherent_args)
-                .into_iter()
-                .map(|(pred, _)| goal.with(tcx, pred)),
+                .iter_instantiated(tcx, &inherent_args)
+                .map(|pred| goal.with(tcx, pred)),
         );
 
-        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, inherent_args);
+        let normalized = tcx.type_of(inherent.def_id).instantiate(tcx, &inherent_args);
         self.instantiate_normalizes_to_term(goal, normalized.into());
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
     }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
index 5c5923e9d39..ebc83bef513 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs
@@ -1,35 +1,33 @@
-use crate::traits::specialization_graph::{self, LeafDef, Node};
-
-use super::assembly::structural_traits::AsyncCallableRelevantTypes;
-use super::assembly::{self, structural_traits, Candidate};
-use super::{EvalCtxt, GoalSource};
-use rustc_hir::def_id::DefId;
-use rustc_hir::LangItem;
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::inspect::ProbeKind;
-use rustc_infer::traits::solve::MaybeCause;
-use rustc_infer::traits::Reveal;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
-use rustc_middle::traits::BuiltinImplSource;
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::NormalizesTo;
-use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_middle::ty::{TypeVisitableExt, Upcast};
-use rustc_span::{ErrorGuaranteed, DUMMY_SP};
-
 mod anon_const;
 mod inherent;
 mod opaque_types;
 mod weak_types;
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::lang_items::TraitSolverLangItem;
+use rustc_type_ir::Upcast as _;
+use rustc_type_ir::{self as ty, Interner, NormalizesTo};
+use tracing::instrument;
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
+use crate::solve::assembly::{self, Candidate};
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
+    NoSolution, QueryResult,
+};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn compute_normalizes_to_goal(
         &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         self.set_is_normalizes_to_goal();
         debug_assert!(self.term_is_fully_unconstrained(goal));
         let normalize_result = self
@@ -49,10 +47,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// Normalize the given alias by at least one step. If the alias is rigid, this
     /// returns `NoSolution`.
     #[instrument(level = "trace", skip(self), ret)]
-    fn normalize_at_least_one_step(
-        &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+    fn normalize_at_least_one_step(&mut self, goal: Goal<I, NormalizesTo<I>>) -> QueryResult<I> {
         match goal.predicate.alias.kind(self.interner()) {
             ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst => {
                 let candidates = self.assemble_and_evaluate_candidates(goal);
@@ -72,38 +67,42 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// emit nested `AliasRelate` goals to structurally normalize the alias.
     pub fn instantiate_normalizes_to_term(
         &mut self,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-        term: ty::Term<'tcx>,
+        goal: Goal<I, NormalizesTo<I>>,
+        term: I::Term,
     ) {
         self.eq(goal.param_env, goal.predicate.term, term)
             .expect("expected goal term to be fully unconstrained");
     }
 }
 
-impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
-    fn self_ty(self) -> Ty<'tcx> {
+impl<Infcx, I> assembly::GoalKind<Infcx> for NormalizesTo<I>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    fn self_ty(self) -> I::Ty {
         self.self_ty()
     }
 
-    fn trait_ref(self, tcx: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+    fn trait_ref(self, tcx: I) -> ty::TraitRef<I> {
         self.alias.trait_ref(tcx)
     }
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
         self.with_self_ty(tcx, self_ty)
     }
 
-    fn trait_def_id(self, tcx: TyCtxt<'tcx>) -> DefId {
+    fn trait_def_id(self, tcx: I) -> I::DefId {
         self.trait_def_id(tcx)
     }
 
     fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if let Some(projection_pred) = assumption.as_projection_clause() {
             if projection_pred.projection_def_id() == goal.predicate.def_id() {
                 let tcx = ecx.interner();
@@ -121,9 +120,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                     // Add GAT where clauses from the trait's definition
                     ecx.add_goals(
                         GoalSource::Misc,
-                        tcx.predicates_of(goal.predicate.def_id())
-                            .instantiate_own(tcx, goal.predicate.alias.args)
-                            .map(|(pred, _)| goal.with(tcx, pred)),
+                        tcx.own_predicates_of(goal.predicate.def_id())
+                            .iter_instantiated(tcx, &goal.predicate.alias.args)
+                            .map(|pred| goal.with(tcx, pred)),
                     );
 
                     then(ecx)
@@ -137,24 +136,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, NormalizesTo<'tcx>>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, NormalizesTo<I>>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
         let goal_trait_ref = goal.predicate.alias.trait_ref(tcx);
-        let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
-        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
-        if !drcx.args_may_unify(
-            goal.predicate.trait_ref(tcx).args,
-            impl_trait_header.trait_ref.skip_binder().args,
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+        if !ecx.interner().args_may_unify_deep(
+            goal.predicate.alias.trait_ref(tcx).args,
+            impl_trait_ref.skip_binder().args,
         ) {
             return Err(NoSolution);
         }
 
         // We have to ignore negative impls when projecting.
-        let impl_polarity = impl_trait_header.polarity;
+        let impl_polarity = tcx.impl_polarity(impl_def_id);
         match impl_polarity {
             ty::ImplPolarity::Negative => return Err(NoSolution),
             ty::ImplPolarity::Reservation => {
@@ -165,30 +163,28 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
-            let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
+            let impl_trait_ref = impl_trait_ref.instantiate(tcx, &impl_args);
 
             ecx.eq(goal.param_env, goal_trait_ref, impl_trait_ref)?;
 
             let where_clause_bounds = tcx
                 .predicates_of(impl_def_id)
-                .instantiate(tcx, impl_args)
-                .predicates
-                .into_iter()
+                .iter_instantiated(tcx, &impl_args)
                 .map(|pred| goal.with(tcx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
             // Add GAT where clauses from the trait's definition
             ecx.add_goals(
                 GoalSource::Misc,
-                tcx.predicates_of(goal.predicate.def_id())
-                    .instantiate_own(tcx, goal.predicate.alias.args)
-                    .map(|(pred, _)| goal.with(tcx, pred)),
+                tcx.own_predicates_of(goal.predicate.def_id())
+                    .iter_instantiated(tcx, &goal.predicate.alias.args)
+                    .map(|pred| goal.with(tcx, pred)),
             );
 
             // In case the associated item is hidden due to specialization, we have to
             // return ambiguity this would otherwise be incomplete, resulting in
             // unsoundness during coherence (#105782).
-            let Some(assoc_def) = ecx.fetch_eligible_assoc_item_def(
+            let Some(target_item_def_id) = ecx.fetch_eligible_assoc_item(
                 goal.param_env,
                 goal_trait_ref,
                 goal.predicate.def_id(),
@@ -198,21 +194,23 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
             };
 
-            let error_response = |ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>, reason| {
-                let guar = tcx.dcx().span_delayed_bug(tcx.def_span(assoc_def.item.def_id), reason);
+            let error_response = |ecx: &mut EvalCtxt<'_, Infcx>, msg: &str| {
+                let guar = tcx.delay_bug(msg);
                 let error_term = match goal.predicate.alias.kind(tcx) {
                     ty::AliasTermKind::ProjectionTy => Ty::new_error(tcx, guar).into(),
-                    ty::AliasTermKind::ProjectionConst => ty::Const::new_error(tcx, guar).into(),
-                    kind => bug!("expected projection, found {kind:?}"),
+                    ty::AliasTermKind::ProjectionConst => Const::new_error(tcx, guar).into(),
+                    kind => panic!("expected projection, found {kind:?}"),
                 };
                 ecx.instantiate_normalizes_to_term(goal, error_term);
                 ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             };
 
-            if !assoc_def.item.defaultness(tcx).has_value() {
-                return error_response(ecx, "missing value for assoc item in impl");
+            if !tcx.has_item_definition(target_item_def_id) {
+                return error_response(ecx, "missing item");
             }
 
+            let target_container_def_id = tcx.parent(target_item_def_id);
+
             // Getting the right args here is complex, e.g. given:
             // - a goal `<Vec<u32> as Trait<i32>>::Assoc<u64>`
             // - the applicable impl `impl<T> Trait<i32> for Vec<T>`
@@ -223,39 +221,40 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             //
             // And then map these args to the args of the defining impl of `Assoc`, going
             // from `[u32, u64]` to `[u32, i32, u64]`.
-            let associated_item_args =
-                ecx.translate_args(&assoc_def, goal, impl_def_id, impl_args, impl_trait_ref)?;
-
-            if !tcx.check_args_compatible(assoc_def.item.def_id, associated_item_args) {
-                return error_response(
-                    ecx,
-                    "associated item has mismatched generic item arguments",
-                );
+            let target_args = ecx.translate_args(
+                goal,
+                impl_def_id,
+                impl_args,
+                impl_trait_ref,
+                target_container_def_id,
+            )?;
+
+            if !tcx.check_args_compatible(target_item_def_id, target_args) {
+                return error_response(ecx, "associated item has mismatched arguments");
             }
 
             // Finally we construct the actual value of the associated type.
             let term = match goal.predicate.alias.kind(tcx) {
                 ty::AliasTermKind::ProjectionTy => {
-                    tcx.type_of(assoc_def.item.def_id).map_bound(|ty| ty.into())
+                    tcx.type_of(target_item_def_id).map_bound(|ty| ty.into())
                 }
                 ty::AliasTermKind::ProjectionConst => {
-                    if tcx.features().associated_const_equality {
-                        bug!("associated const projection is not supported yet")
+                    if tcx.features().associated_const_equality() {
+                        panic!("associated const projection is not supported yet")
                     } else {
                         ty::EarlyBinder::bind(
-                            ty::Const::new_error_with_message(
+                            Const::new_error_with_message(
                                 tcx,
-                                DUMMY_SP,
                                 "associated const projection is not supported yet",
                             )
                             .into(),
                         )
                     }
                 }
-                kind => bug!("expected projection, found {kind:?}"),
+                kind => panic!("expected projection, found {kind:?}"),
             };
 
-            ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, associated_item_args));
+            ecx.instantiate_normalizes_to_term(goal, term.instantiate(tcx, &target_args));
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
@@ -263,63 +262,60 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     /// Fail to normalize if the predicate contains an error, alternatively, we could normalize to `ty::Error`
     /// and succeed. Can experiment with this to figure out what results in better error messages.
     fn consider_error_guaranteed_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        _guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution> {
         Err(NoSolution)
     }
 
     fn consider_auto_trait_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        ecx.interner().dcx().span_delayed_bug(
-            ecx.interner().def_span(goal.predicate.def_id()),
-            "associated types not allowed on auto traits",
-        );
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        _goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        ecx.interner().delay_bug("associated types not allowed on auto traits");
         Err(NoSolution)
     }
 
     fn consider_trait_alias_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("trait aliases do not have associated types: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("trait aliases do not have associated types: {:?}", goal);
     }
 
     fn consider_builtin_sized_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Sized` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Sized` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_copy_clone_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Copy`/`Clone` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointer_like_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`PointerLike` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`PointerLike` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_fn_ptr_trait_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`FnPtr` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`FnPtr` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
         let tupled_inputs_and_output =
             match structural_traits::extract_tupled_inputs_and_output_from_callable(
@@ -333,7 +329,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output])
+            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
@@ -359,16 +355,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
         let env_region = match goal_kind {
             ty::ClosureKind::Fn | ty::ClosureKind::FnMut => goal.predicate.alias.args.region_at(2),
             // Doesn't matter what this region is
-            ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
+            ty::ClosureKind::FnOnce => Region::new_static(tcx),
         };
         let (tupled_inputs_and_output_and_coroutine, nested_preds) =
             structural_traits::extract_tupled_inputs_and_output_from_async_callable(
@@ -379,7 +375,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty: output_ty, .. }| {
-                ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output_ty])
+                ty::TraitRef::new(
+                    tcx,
+                    tcx.require_lang_item(TraitSolverLangItem::Sized),
+                    [output_ty],
+                )
             },
         );
 
@@ -391,7 +391,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                      coroutine_return_ty,
                  }| {
                     let (projection_term, term) = if tcx
-                        .is_lang_item(goal.predicate.def_id(), LangItem::CallOnceFuture)
+                        .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallOnceFuture)
                     {
                         (
                             ty::AliasTerm::new(
@@ -401,34 +401,41 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CallRefFuture) {
+                    } else if tcx
+                        .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CallRefFuture)
+                    {
                         (
                             ty::AliasTerm::new(
                                 tcx,
                                 goal.predicate.def_id(),
                                 [
-                                    ty::GenericArg::from(goal.predicate.self_ty()),
+                                    I::GenericArg::from(goal.predicate.self_ty()),
                                     tupled_inputs_ty.into(),
                                     env_region.into(),
                                 ],
                             ),
                             output_coroutine_ty.into(),
                         )
-                    } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::AsyncFnOnceOutput)
-                    {
+                    } else if tcx.is_lang_item(
+                        goal.predicate.def_id(),
+                        TraitSolverLangItem::AsyncFnOnceOutput,
+                    ) {
                         (
                             ty::AliasTerm::new(
                                 tcx,
                                 goal.predicate.def_id(),
                                 [
-                                    ty::GenericArg::from(goal.predicate.self_ty()),
+                                    I::GenericArg::from(goal.predicate.self_ty()),
                                     tupled_inputs_ty.into(),
                                 ],
                             ),
                             coroutine_return_ty.into(),
                         )
                     } else {
-                        bug!("no such associated type in `AsyncFn*`: {:?}", goal.predicate.def_id())
+                        panic!(
+                            "no such associated type in `AsyncFn*`: {:?}",
+                            goal.predicate.def_id()
+                        )
                     };
                     ty::ProjectionPredicate { projection_term, term }
                 },
@@ -450,9 +457,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let [
             closure_fn_kind_ty,
             goal_kind_ty,
@@ -462,7 +469,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             coroutine_captures_by_ref_ty,
         ] = **goal.predicate.alias.args
         else {
-            bug!();
+            panic!();
         };
 
         // Bail if the upvars haven't been constrained.
@@ -497,18 +504,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_tuple_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Tuple` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Tuple` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
-        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        let metadata_def_id = tcx.require_lang_item(TraitSolverLangItem::Metadata);
         assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
@@ -530,16 +537,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 | ty::CoroutineWitness(..)
                 | ty::Never
                 | ty::Foreign(..)
-                | ty::Dynamic(_, _, ty::DynStar) => tcx.types.unit,
+                | ty::Dynamic(_, _, ty::DynStar) => Ty::new_unit(tcx),
 
-                ty::Error(e) => Ty::new_error(tcx, *e),
+                ty::Error(e) => Ty::new_error(tcx, e),
 
-                ty::Str | ty::Slice(_) => tcx.types.usize,
+                ty::Str | ty::Slice(_) => Ty::new_usize(tcx),
 
                 ty::Dynamic(_, _, ty::Dyn) => {
-                    let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
+                    let dyn_metadata = tcx.require_lang_item(TraitSolverLangItem::DynMetadata);
                     tcx.type_of(dyn_metadata)
-                        .instantiate(tcx, &[ty::GenericArg::from(goal.predicate.self_ty())])
+                        .instantiate(tcx, &[I::GenericArg::from(goal.predicate.self_ty())])
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
@@ -549,32 +556,31 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                     // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::new(
                         tcx,
-                        tcx.require_lang_item(LangItem::Sized, None),
-                        [ty::GenericArg::from(goal.predicate.self_ty())],
+                        tcx.require_lang_item(TraitSolverLangItem::Sized),
+                        [I::GenericArg::from(goal.predicate.self_ty())],
                     );
                     // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
                     ecx.add_goal(GoalSource::Misc, goal.with(tcx, sized_predicate));
-                    tcx.types.unit
+                    Ty::new_unit(tcx)
                 }
 
-                ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
-                    None => tcx.types.unit,
-                    Some(tail_def) => {
-                        let tail_ty = tail_def.ty(tcx, args);
-                        Ty::new_projection(tcx, metadata_def_id, [tail_ty])
+                ty::Adt(def, args) if def.is_struct() => match def.struct_tail_ty(tcx) {
+                    None => Ty::new_unit(tcx),
+                    Some(tail_ty) => {
+                        Ty::new_projection(tcx, metadata_def_id, [tail_ty.instantiate(tcx, &args)])
                     }
                 },
-                ty::Adt(_, _) => tcx.types.unit,
+                ty::Adt(_, _) => Ty::new_unit(tcx),
 
                 ty::Tuple(elements) => match elements.last() {
-                    None => tcx.types.unit,
+                    None => Ty::new_unit(tcx),
                     Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
                     ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_),
                 )
-                | ty::Bound(..) => bug!(
+                | ty::Bound(..) => panic!(
                     "unexpected self ty `{:?}` when normalizing `<T as Pointee>::Metadata`",
                     goal.predicate.self_ty()
                 ),
@@ -586,11 +592,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_future_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -622,11 +628,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -658,18 +664,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_fused_iterator_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`FusedIterator` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`FusedIterator` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -685,10 +691,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             // coroutine yield ty `Poll<Option<I>>`.
             let wrapped_expected_ty = Ty::new_adt(
                 tcx,
-                tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)),
+                tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Poll)),
                 tcx.mk_args(&[Ty::new_adt(
                     tcx,
-                    tcx.adt_def(tcx.require_lang_item(LangItem::Option, None)),
+                    tcx.adt_def(tcx.require_lang_item(TraitSolverLangItem::Option)),
                     tcx.mk_args(&[expected_ty.into()]),
                 )
                 .into()]),
@@ -701,11 +707,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_coroutine_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -717,15 +723,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
         let coroutine = args.as_coroutine();
 
-        let term = if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineReturn) {
+        let term = if tcx
+            .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineReturn)
+        {
             coroutine.return_ty().into()
-        } else if tcx.is_lang_item(goal.predicate.def_id(), LangItem::CoroutineYield) {
+        } else if tcx.is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::CoroutineYield) {
             coroutine.yield_ty().into()
         } else {
-            bug!(
-                "unexpected associated item `<{self_ty} as Coroutine>::{}`",
-                tcx.item_name(goal.predicate.def_id())
-            )
+            panic!("unexpected associated item `{:?}` for `{self_ty:?}`", goal.predicate.def_id())
         };
 
         Self::probe_and_consider_implied_clause(
@@ -748,18 +753,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_structural_builtin_unsize_candidates(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
-        bug!("`Unsize` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>> {
+        panic!("`Unsize` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let discriminant_ty = match *self_ty.kind() {
+        let discriminant_ty = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(..)
@@ -794,7 +799,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
             }
 
             ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
-            | ty::Bound(..) => bug!(
+            | ty::Bound(..) => panic!(
                 "unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`",
                 goal.predicate.self_ty()
             ),
@@ -807,11 +812,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
-        let async_destructor_ty = match *self_ty.kind() {
+        let async_destructor_ty = match self_ty.kind() {
             ty::Bool
             | ty::Char
             | ty::Int(..)
@@ -842,12 +847,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
             ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))
             | ty::Foreign(..)
-            | ty::Bound(..) => bug!(
+            | ty::Bound(..) => panic!(
                 "unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`",
                 goal.predicate.self_ty()
             ),
 
-            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => bug!(
+            ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!(
                 "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}"
             ),
         };
@@ -860,93 +865,56 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
     }
 
     fn consider_builtin_destruct_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`Destruct` does not have an associated type: {:?}", goal);
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`Destruct` does not have an associated type: {:?}", goal);
     }
 
     fn consider_builtin_transmute_candidate(
-        _ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
-        bug!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
+        _ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
+        panic!("`BikeshedIntrinsicFrom` does not have an associated type: {:?}", goal)
     }
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     fn translate_args(
         &mut self,
-        assoc_def: &LeafDef,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-        impl_def_id: DefId,
-        impl_args: ty::GenericArgsRef<'tcx>,
-        impl_trait_ref: rustc_type_ir::TraitRef<TyCtxt<'tcx>>,
-    ) -> Result<ty::GenericArgsRef<'tcx>, NoSolution> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+        impl_def_id: I::DefId,
+        impl_args: I::GenericArgs,
+        impl_trait_ref: rustc_type_ir::TraitRef<I>,
+        target_container_def_id: I::DefId,
+    ) -> Result<I::GenericArgs, NoSolution> {
         let tcx = self.interner();
-        Ok(match assoc_def.defining_node {
-            Node::Trait(_) => goal.predicate.alias.args,
-            Node::Impl(target_impl_def_id) => {
-                if target_impl_def_id == impl_def_id {
-                    // Same impl, no need to fully translate, just a rebase from
-                    // the trait is sufficient.
-                    goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
-                } else {
-                    let target_args = self.fresh_args_for_item(target_impl_def_id);
-                    let target_trait_ref = tcx
-                        .impl_trait_ref(target_impl_def_id)
-                        .unwrap()
-                        .instantiate(tcx, target_args);
-                    // Relate source impl to target impl by equating trait refs.
-                    self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
-                    // Also add predicates since they may be needed to constrain the
-                    // target impl's params.
-                    self.add_goals(
-                        GoalSource::Misc,
-                        tcx.predicates_of(target_impl_def_id)
-                            .instantiate(tcx, target_args)
-                            .into_iter()
-                            .map(|(pred, _)| goal.with(tcx, pred)),
-                    );
-                    goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
-                }
-            }
-        })
-    }
-
-    /// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
-    ///
-    /// FIXME: We should merge these 3 implementations as it's likely that they otherwise
-    /// diverge.
-    #[instrument(level = "trace", skip(self, param_env), ret)]
-    fn fetch_eligible_assoc_item_def(
-        &self,
-        param_env: ty::ParamEnv<'tcx>,
-        goal_trait_ref: ty::TraitRef<'tcx>,
-        trait_assoc_def_id: DefId,
-        impl_def_id: DefId,
-    ) -> Result<Option<LeafDef>, NoSolution> {
-        let node_item =
-            specialization_graph::assoc_def(self.interner(), impl_def_id, trait_assoc_def_id)
-                .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
-
-        let eligible = if node_item.is_final() {
-            // Non-specializable items are always projectable.
-            true
+        Ok(if target_container_def_id == impl_trait_ref.def_id {
+            // Default value from the trait definition. No need to rebase.
+            goal.predicate.alias.args
+        } else if target_container_def_id == impl_def_id {
+            // Same impl, no need to fully translate, just a rebase from
+            // the trait is sufficient.
+            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, impl_args)
         } else {
-            // Only reveal a specializable default if we're past type-checking
-            // and the obligation is monomorphic, otherwise passes such as
-            // transmute checking and polymorphic MIR optimizations could
-            // get a result which isn't correct for all monomorphizations.
-            if param_env.reveal() == Reveal::All {
-                let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
-                !poly_trait_ref.still_further_specializable()
-            } else {
-                trace!(?node_item.item.def_id, "not eligible due to default");
-                false
-            }
-        };
-
-        if eligible { Ok(Some(node_item)) } else { Ok(None) }
+            let target_args = self.fresh_args_for_item(target_container_def_id);
+            let target_trait_ref =
+                tcx.impl_trait_ref(target_container_def_id).instantiate(tcx, &target_args);
+            // Relate source impl to target impl by equating trait refs.
+            self.eq(goal.param_env, impl_trait_ref, target_trait_ref)?;
+            // Also add predicates since they may be needed to constrain the
+            // target impl's params.
+            self.add_goals(
+                GoalSource::Misc,
+                tcx.predicates_of(target_container_def_id)
+                    .iter_instantiated(tcx, &target_args)
+                    .map(|pred| goal.with(tcx, pred)),
+            );
+            goal.predicate.alias.args.rebase_onto(tcx, impl_trait_ref.def_id, target_args)
+        })
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index 82464470b2a..710671b45d0 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -1,20 +1,23 @@
 //! Computes a normalizes-to (projection) goal for opaque types. This goal
 //! behaves differently depending on the param-env's reveal mode and whether
 //! the opaque is in a defining scope.
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::traits::Reveal;
-use rustc_middle::ty;
-use rustc_middle::ty::util::NotUniqueParam;
 
-use crate::solve::{EvalCtxt, SolverMode};
+use rustc_index::bit_set::GrowableBitSet;
+use rustc_type_ir::inherent::*;
+use rustc_type_ir::{self as ty, Interner};
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, NoSolution, QueryResult, Reveal, SolverMode};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_opaque_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let opaque_ty = goal.predicate.alias;
         let expected = goal.predicate.term.as_type().expect("no such thing as an opaque const");
@@ -31,7 +34,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     return Err(NoSolution);
                 }
                 // FIXME: This may have issues when the args contain aliases...
-                match self.interner().uses_unique_placeholders_ignoring_regions(opaque_ty.args) {
+                match uses_unique_placeholders_ignoring_regions(self.interner(), opaque_ty.args) {
                     Err(NotUniqueParam::NotParam(param)) if param.is_non_region_infer() => {
                         return self.evaluate_added_goals_and_make_canonical_response(
                             Certainty::AMBIGUOUS,
@@ -60,6 +63,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 }
 
                 // Otherwise, define a new opaque type
+                // FIXME: should we use `inject_hidden_type_unchecked` here?
                 self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
                 self.add_item_bounds_for_hidden_type(
                     opaque_ty.def_id,
@@ -82,10 +86,51 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             }
             (Reveal::All, _) => {
                 // FIXME: Add an assertion that opaque type storage is empty.
-                let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, opaque_ty.args);
+                let actual = tcx.type_of(opaque_ty.def_id).instantiate(tcx, &opaque_ty.args);
                 self.eq(goal.param_env, expected, actual)?;
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
         }
     }
 }
+
+/// Checks whether each generic argument is simply a unique generic placeholder.
+///
+/// FIXME: Interner argument is needed to constrain the `I` parameter.
+pub fn uses_unique_placeholders_ignoring_regions<I: Interner>(
+    _interner: I,
+    args: I::GenericArgs,
+) -> Result<(), NotUniqueParam<I>> {
+    let mut seen = GrowableBitSet::default();
+    for arg in args {
+        match arg.kind() {
+            // Ignore regions, since we can't resolve those in a canonicalized
+            // query in the trait solver.
+            ty::GenericArgKind::Lifetime(_) => {}
+            ty::GenericArgKind::Type(t) => match t.kind() {
+                ty::Placeholder(p) => {
+                    if !seen.insert(p.var()) {
+                        return Err(NotUniqueParam::DuplicateParam(t.into()));
+                    }
+                }
+                _ => return Err(NotUniqueParam::NotParam(t.into())),
+            },
+            ty::GenericArgKind::Const(c) => match c.kind() {
+                ty::ConstKind::Placeholder(p) => {
+                    if !seen.insert(p.var()) {
+                        return Err(NotUniqueParam::DuplicateParam(c.into()));
+                    }
+                }
+                _ => return Err(NotUniqueParam::NotParam(c.into())),
+            },
+        }
+    }
+
+    Ok(())
+}
+
+// FIXME: This should check for dupes and non-params first, then infer vars.
+pub enum NotUniqueParam<I: Interner> {
+    DuplicateParam(I::GenericArg),
+    NotParam(I::GenericArg),
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
index 5442b9ccffc..45341917bb2 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/weak_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/weak_types.rs
@@ -3,17 +3,21 @@
 //!
 //! Since a weak alias is never ambiguous, this just computes the `type_of` of
 //! the alias and registers the where-clauses of the type alias.
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, QueryResult};
-use rustc_middle::ty;
 
-use crate::solve::EvalCtxt;
+use rustc_type_ir::{self as ty, Interner};
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
+
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     pub(super) fn normalize_weak_type(
         &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ty::NormalizesTo<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let weak_ty = goal.predicate.alias;
 
@@ -21,13 +25,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         self.add_goals(
             GoalSource::Misc,
             tcx.predicates_of(weak_ty.def_id)
-                .instantiate(tcx, weak_ty.args)
-                .predicates
-                .into_iter()
+                .iter_instantiated(tcx, &weak_ty.args)
                 .map(|pred| goal.with(tcx, pred)),
         );
 
-        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, weak_ty.args);
+        let actual = tcx.type_of(weak_ty.def_id).instantiate(tcx, &weak_ty.args);
         self.instantiate_normalizes_to_term(goal, actual.into());
 
         self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
index cae73cc2d07..4bb1fe5be6f 100644
--- a/compiler/rustc_trait_selection/src/solve/project_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/project_goals.rs
@@ -1,16 +1,19 @@
-use crate::solve::GoalSource;
+use rustc_type_ir::{self as ty, Interner, ProjectionPredicate};
+use tracing::instrument;
 
-use super::EvalCtxt;
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty::{self, ProjectionPredicate};
+use crate::infcx::SolverDelegate;
+use crate::solve::{Certainty, EvalCtxt, Goal, GoalSource, QueryResult};
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     #[instrument(level = "trace", skip(self), ret)]
     pub(super) fn compute_projection_goal(
         &mut self,
-        goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, ProjectionPredicate<I>>,
+    ) -> QueryResult<I> {
         let tcx = self.interner();
         let projection_term = goal.predicate.projection_term.to_term(tcx);
         let goal = goal.with(
diff --git a/compiler/rustc_trait_selection/src/solve/search_graph.rs b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
index 84878fea101..b923a121d81 100644
--- a/compiler/rustc_trait_selection/src/solve/search_graph.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/search_graph.rs
@@ -1,22 +1,19 @@
 use std::mem;
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_index::Idx;
-use rustc_index::IndexVec;
-use rustc_infer::infer::InferCtxt;
-use rustc_middle::dep_graph::dep_kinds;
-use rustc_middle::traits::solve::CacheData;
-use rustc_middle::traits::solve::EvaluationCache;
-use rustc_middle::ty::TyCtxt;
-use rustc_next_trait_solver::solve::{CanonicalInput, Certainty, QueryResult};
-use rustc_session::Limit;
+use rustc_index::{Idx, IndexVec};
 use rustc_type_ir::inherent::*;
 use rustc_type_ir::Interner;
+use tracing::debug;
 
-use super::inspect;
-use super::inspect::ProofTreeBuilder;
-use super::SolverMode;
-use crate::solve::FIXPOINT_STEP_LIMIT;
+use crate::infcx::SolverDelegate;
+use crate::solve::inspect::{self, ProofTreeBuilder};
+use crate::solve::{
+    CacheData, CanonicalInput, Certainty, QueryResult, SolverMode, FIXPOINT_STEP_LIMIT,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct SolverLimit(usize);
 
 rustc_index::newtype_index! {
     #[orderable]
@@ -39,7 +36,7 @@ bitflags::bitflags! {
 struct StackEntry<I: Interner> {
     input: CanonicalInput<I>,
 
-    available_depth: Limit,
+    available_depth: SolverLimit,
 
     /// The maximum depth reached by this stack entry, only up-to date
     /// for the top of the stack and lazily updated for the rest.
@@ -168,19 +165,19 @@ impl<I: Interner> SearchGraph<I> {
     fn allowed_depth_for_nested(
         tcx: I,
         stack: &IndexVec<StackDepth, StackEntry<I>>,
-    ) -> Option<Limit> {
+    ) -> Option<SolverLimit> {
         if let Some(last) = stack.raw.last() {
             if last.available_depth.0 == 0 {
                 return None;
             }
 
             Some(if last.encountered_overflow {
-                Limit(last.available_depth.0 / 4)
+                SolverLimit(last.available_depth.0 / 4)
             } else {
-                Limit(last.available_depth.0 - 1)
+                SolverLimit(last.available_depth.0 - 1)
             })
         } else {
-            Some(Limit(tcx.recursion_limit()))
+            Some(SolverLimit(tcx.recursion_limit()))
         }
     }
 
@@ -240,34 +237,26 @@ impl<I: Interner> SearchGraph<I> {
             !entry.is_empty()
         });
     }
-}
 
-impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
     /// The trait solver behavior is different for coherence
     /// so we use a separate cache. Alternatively we could use
     /// a single cache and share it between coherence and ordinary
     /// trait solving.
-    pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> {
-        match self.mode {
-            SolverMode::Normal => &tcx.new_solver_evaluation_cache,
-            SolverMode::Coherence => &tcx.new_solver_coherence_evaluation_cache,
-        }
+    pub(super) fn global_cache(&self, tcx: I) -> I::EvaluationCache {
+        tcx.evaluation_cache(self.mode)
     }
 
     /// Probably the most involved method of the whole solver.
     ///
     /// Given some goal which is proven via the `prove_goal` closure, this
     /// handles caching, overflow, and coinductive cycles.
-    pub(super) fn with_new_goal(
+    pub(super) fn with_new_goal<Infcx: SolverDelegate<Interner = I>>(
         &mut self,
-        tcx: TyCtxt<'tcx>,
-        input: CanonicalInput<TyCtxt<'tcx>>,
-        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
-        mut prove_goal: impl FnMut(
-            &mut Self,
-            &mut ProofTreeBuilder<InferCtxt<'tcx>>,
-        ) -> QueryResult<TyCtxt<'tcx>>,
-    ) -> QueryResult<TyCtxt<'tcx>> {
+        tcx: I,
+        input: CanonicalInput<I>,
+        inspect: &mut ProofTreeBuilder<Infcx>,
+        mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<Infcx>) -> QueryResult<I>,
+    ) -> QueryResult<I> {
         self.check_invariants();
         // Check for overflow.
         let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else {
@@ -361,21 +350,20 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
         // not tracked by the cache key and from outside of this anon task, it
         // must not be added to the global cache. Notably, this is the case for
         // trait solver cycles participants.
-        let ((final_entry, result), dep_node) =
-            tcx.dep_graph.with_anon_task(tcx, dep_kinds::TraitSelect, || {
-                for _ in 0..FIXPOINT_STEP_LIMIT {
-                    match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) {
-                        StepResult::Done(final_entry, result) => return (final_entry, result),
-                        StepResult::HasChanged => debug!("fixpoint changed provisional results"),
-                    }
+        let ((final_entry, result), dep_node) = tcx.with_cached_task(|| {
+            for _ in 0..FIXPOINT_STEP_LIMIT {
+                match self.fixpoint_step_in_task(tcx, input, inspect, &mut prove_goal) {
+                    StepResult::Done(final_entry, result) => return (final_entry, result),
+                    StepResult::HasChanged => debug!("fixpoint changed provisional results"),
                 }
+            }
 
-                debug!("canonical cycle overflow");
-                let current_entry = self.pop_stack();
-                debug_assert!(current_entry.has_been_used.is_empty());
-                let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false));
-                (current_entry, result)
-            });
+            debug!("canonical cycle overflow");
+            let current_entry = self.pop_stack();
+            debug_assert!(current_entry.has_been_used.is_empty());
+            let result = Self::response_no_constraints(tcx, input, Certainty::overflow(false));
+            (current_entry, result)
+        });
 
         let proof_tree = inspect.finalize_canonical_goal_evaluation(tcx);
 
@@ -423,16 +411,17 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
     /// Try to fetch a previously computed result from the global cache,
     /// making sure to only do so if it would match the result of reevaluating
     /// this goal.
-    fn lookup_global_cache(
+    fn lookup_global_cache<Infcx: SolverDelegate<Interner = I>>(
         &mut self,
-        tcx: TyCtxt<'tcx>,
-        input: CanonicalInput<TyCtxt<'tcx>>,
-        available_depth: Limit,
-        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
-    ) -> Option<QueryResult<TyCtxt<'tcx>>> {
+        tcx: I,
+        input: CanonicalInput<I>,
+        available_depth: SolverLimit,
+        inspect: &mut ProofTreeBuilder<Infcx>,
+    ) -> Option<QueryResult<I>> {
         let CacheData { result, proof_tree, additional_depth, encountered_overflow } = self
             .global_cache(tcx)
-            .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth)?;
+            // FIXME: Awkward `Limit -> usize -> Limit`.
+            .get(tcx, input, self.stack.iter().map(|e| e.input), available_depth.0)?;
 
         // If we're building a proof tree and the current cache entry does not
         // contain a proof tree, we do not use the entry but instead recompute
@@ -465,21 +454,22 @@ enum StepResult<I: Interner> {
     HasChanged,
 }
 
-impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
+impl<I: Interner> SearchGraph<I> {
     /// When we encounter a coinductive cycle, we have to fetch the
     /// result of that cycle while we are still computing it. Because
     /// of this we continuously recompute the cycle until the result
     /// of the previous iteration is equal to the final result, at which
     /// point we are done.
-    fn fixpoint_step_in_task<F>(
+    fn fixpoint_step_in_task<Infcx, F>(
         &mut self,
-        tcx: TyCtxt<'tcx>,
-        input: CanonicalInput<TyCtxt<'tcx>>,
-        inspect: &mut ProofTreeBuilder<InferCtxt<'tcx>>,
+        tcx: I,
+        input: CanonicalInput<I>,
+        inspect: &mut ProofTreeBuilder<Infcx>,
         prove_goal: &mut F,
-    ) -> StepResult<TyCtxt<'tcx>>
+    ) -> StepResult<I>
     where
-        F: FnMut(&mut Self, &mut ProofTreeBuilder<InferCtxt<'tcx>>) -> QueryResult<TyCtxt<'tcx>>,
+        Infcx: SolverDelegate<Interner = I>,
+        F: FnMut(&mut Self, &mut ProofTreeBuilder<Infcx>) -> QueryResult<I>,
     {
         let result = prove_goal(self, inspect);
         let stack_entry = self.pop_stack();
@@ -533,15 +523,13 @@ impl<'tcx> SearchGraph<TyCtxt<'tcx>> {
     }
 
     fn response_no_constraints(
-        tcx: TyCtxt<'tcx>,
-        goal: CanonicalInput<TyCtxt<'tcx>>,
+        tcx: I,
+        goal: CanonicalInput<I>,
         certainty: Certainty,
-    ) -> QueryResult<TyCtxt<'tcx>> {
+    ) -> QueryResult<I> {
         Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty))
     }
-}
 
-impl<I: Interner> SearchGraph<I> {
     #[allow(rustc::potential_query_instability)]
     fn check_invariants(&self) {
         if !cfg!(debug_assertions) {
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index bcb2ea18f78..d1419bf5db9 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -1,60 +1,60 @@
 //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
 
-use super::assembly::structural_traits::AsyncCallableRelevantTypes;
-use super::assembly::{self, structural_traits, Candidate};
-use super::{EvalCtxt, GoalSource, SolverMode};
+use rustc_ast_ir::Movability;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_hir::def_id::DefId;
-use rustc_hir::{LangItem, Movability};
-use rustc_infer::infer::InferCtxt;
-use rustc_infer::traits::query::NoSolution;
-use rustc_infer::traits::solve::MaybeCause;
-use rustc_infer::traits::util::supertraits;
-use rustc_middle::bug;
-use rustc_middle::traits::solve::inspect::ProbeKind;
-use rustc_middle::traits::solve::{CandidateSource, Certainty, Goal, QueryResult};
-use rustc_middle::traits::{BuiltinImplSource, Reveal};
-use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
-use rustc_middle::ty::{self, Ty, TyCtxt, Upcast};
-use rustc_middle::ty::{TraitPredicate, TypeVisitableExt};
-use rustc_span::ErrorGuaranteed;
-
-impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
-    fn self_ty(self) -> Ty<'tcx> {
+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 tracing::{instrument, trace};
+
+use crate::infcx::SolverDelegate;
+use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
+use crate::solve::assembly::{self, Candidate};
+use crate::solve::inspect::ProbeKind;
+use crate::solve::{
+    BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
+    NoSolution, QueryResult, Reveal, SolverMode,
+};
+
+impl<Infcx, I> assembly::GoalKind<Infcx> for TraitPredicate<I>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
+    fn self_ty(self) -> I::Ty {
         self.self_ty()
     }
 
-    fn trait_ref(self, _: TyCtxt<'tcx>) -> ty::TraitRef<'tcx> {
+    fn trait_ref(self, _: I) -> ty::TraitRef<I> {
         self.trait_ref
     }
 
-    fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
+    fn with_self_ty(self, tcx: I, self_ty: I::Ty) -> Self {
         self.with_self_ty(tcx, self_ty)
     }
 
-    fn trait_def_id(self, _: TyCtxt<'tcx>) -> DefId {
+    fn trait_def_id(self, _: I) -> I::DefId {
         self.def_id()
     }
 
     fn consider_impl_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-        impl_def_id: DefId,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, TraitPredicate<I>>,
+        impl_def_id: I::DefId,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = ecx.interner();
 
-        let impl_trait_header = tcx.impl_trait_header(impl_def_id).unwrap();
-        let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::ForLookup };
-        if !drcx.args_may_unify(
-            goal.predicate.trait_ref.args,
-            impl_trait_header.trait_ref.skip_binder().args,
-        ) {
+        let impl_trait_ref = tcx.impl_trait_ref(impl_def_id);
+        if !tcx
+            .args_may_unify_deep(goal.predicate.trait_ref.args, impl_trait_ref.skip_binder().args)
+        {
             return Err(NoSolution);
         }
 
         // An upper bound of the certainty of this goal, used to lower the certainty
         // of reservation impl to ambiguous during coherence.
-        let impl_polarity = impl_trait_header.polarity;
+        let impl_polarity = tcx.impl_polarity(impl_def_id);
         let maximal_certainty = match (impl_polarity, goal.predicate.polarity) {
             // In intercrate mode, this is ambiguous. But outside of intercrate,
             // it's not a real impl.
@@ -77,14 +77,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.probe_trait_candidate(CandidateSource::Impl(impl_def_id)).enter(|ecx| {
             let impl_args = ecx.fresh_args_for_item(impl_def_id);
             ecx.record_impl_args(impl_args);
-            let impl_trait_ref = impl_trait_header.trait_ref.instantiate(tcx, impl_args);
+            let impl_trait_ref = impl_trait_ref.instantiate(tcx, &impl_args);
 
             ecx.eq(goal.param_env, goal.predicate.trait_ref, impl_trait_ref)?;
             let where_clause_bounds = tcx
                 .predicates_of(impl_def_id)
-                .instantiate(tcx, impl_args)
-                .predicates
-                .into_iter()
+                .iter_instantiated(tcx, &impl_args)
                 .map(|pred| goal.with(tcx, pred));
             ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds);
 
@@ -93,21 +91,21 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_error_guaranteed_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        _guar: ErrorGuaranteed,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        _guar: I::ErrorGuaranteed,
+    ) -> Result<Candidate<I>, NoSolution> {
         // FIXME: don't need to enter a probe here.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
     }
 
     fn probe_and_match_goal_against_assumption(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, Self>,
-        assumption: ty::Clause<'tcx>,
-        then: impl FnOnce(&mut EvalCtxt<'_, InferCtxt<'tcx>>) -> QueryResult<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        source: CandidateSource<I>,
+        goal: Goal<I, Self>,
+        assumption: I::Clause,
+        then: impl FnOnce(&mut EvalCtxt<'_, Infcx>) -> QueryResult<I>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if let Some(trait_clause) = assumption.as_trait_clause() {
             if trait_clause.def_id() == goal.predicate.def_id()
                 && trait_clause.polarity() == goal.predicate.polarity
@@ -130,9 +128,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_auto_trait_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -159,7 +157,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         if let ty::Alias(ty::Opaque, opaque_ty) = goal.predicate.self_ty().kind() {
             if matches!(goal.param_env.reveal(), Reveal::All)
                 || matches!(ecx.solver_mode(), SolverMode::Coherence)
-                || ecx.can_define_opaque_ty(opaque_ty.def_id)
+                || opaque_ty
+                    .def_id
+                    .as_local()
+                    .is_some_and(|def_id| ecx.can_define_opaque_ty(def_id))
             {
                 return Err(NoSolution);
             }
@@ -173,9 +174,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_trait_alias_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -185,20 +186,18 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let nested_obligations = tcx
                 .predicates_of(goal.predicate.def_id())
-                .instantiate(tcx, goal.predicate.trait_ref.args);
+                .iter_instantiated(tcx, &goal.predicate.trait_ref.args)
+                .map(|p| goal.with(tcx, p));
             // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-            ecx.add_goals(
-                GoalSource::Misc,
-                nested_obligations.predicates.into_iter().map(|p| goal.with(tcx, p)),
-            );
+            ecx.add_goals(GoalSource::Misc, nested_obligations);
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
         })
     }
 
     fn consider_builtin_sized_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -211,9 +210,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_copy_clone_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -226,28 +225,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_pointer_like_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        // The regions of a type don't affect the size of the type
         let tcx = ecx.interner();
-        // We should erase regions from both the param-env and type, since both
-        // may have infer regions. Specifically, after canonicalizing and instantiating,
-        // early bound regions turn into region vars in both the new and old solver.
-        let key = tcx.erase_regions(goal.param_env.and(goal.predicate.self_ty()));
         // But if there are inference variables, we have to wait until it's resolved.
-        if key.has_non_region_infer() {
+        if (goal.param_env, goal.predicate.self_ty()).has_non_region_infer() {
             return ecx.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
-        if let Ok(layout) = tcx.layout_of(key)
-            && layout.layout.is_pointer_like(&tcx.data_layout)
-        {
-            // FIXME: We could make this faster by making a no-constraints response
+        if tcx.layout_is_pointer_like(goal.param_env, goal.predicate.self_ty()) {
             ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
                 .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
         } else {
@@ -256,9 +247,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fn_ptr_trait_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let self_ty = goal.predicate.self_ty();
         match goal.predicate.polarity {
             // impl FnPtr for FnPtr {}
@@ -287,10 +278,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -308,7 +299,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 }
             };
         let output_is_sized_pred = tupled_inputs_and_output.map_bound(|(_, output)| {
-            ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [output])
+            ty::TraitRef::new(tcx, tcx.require_lang_item(TraitSolverLangItem::Sized), [output])
         });
 
         let pred = tupled_inputs_and_output
@@ -328,10 +319,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_fn_trait_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
         goal_kind: ty::ClosureKind,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -343,13 +334,13 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 goal.predicate.self_ty(),
                 goal_kind,
                 // This region doesn't matter because we're throwing away the coroutine type
-                tcx.lifetimes.re_static,
+                Region::new_static(tcx),
             )?;
         let output_is_sized_pred = tupled_inputs_and_output_and_coroutine.map_bound(
             |AsyncCallableRelevantTypes { output_coroutine_ty, .. }| {
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Sized, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Sized),
                     [output_coroutine_ty],
                 )
             },
@@ -379,11 +370,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_fn_kind_helper_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let [closure_fn_kind_ty, goal_kind_ty] = **goal.predicate.trait_ref.args else {
-            bug!();
+            panic!();
         };
 
         let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
@@ -406,9 +397,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     /// impl Tuple for (T1, .., Tn) {}
     /// ```
     fn consider_builtin_tuple_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -422,9 +413,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_pointee_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -434,14 +425,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_future_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -460,14 +451,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -486,14 +477,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_fused_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -510,14 +501,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_iterator_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
-        let ty::Coroutine(def_id, _) = *goal.predicate.self_ty().kind() else {
+        let ty::Coroutine(def_id, _) = goal.predicate.self_ty().kind() else {
             return Err(NoSolution);
         };
 
@@ -536,15 +527,15 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_coroutine_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
 
         let self_ty = goal.predicate.self_ty();
-        let ty::Coroutine(def_id, args) = *self_ty.kind() else {
+        let ty::Coroutine(def_id, args) = self_ty.kind() else {
             return Err(NoSolution);
         };
 
@@ -568,9 +559,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_discriminant_kind_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -581,9 +572,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_async_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -594,9 +585,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_destruct_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -610,9 +601,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 
     fn consider_builtin_transmute_candidate(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Result<Candidate<I>, NoSolution> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return Err(NoSolution);
         }
@@ -622,22 +613,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             return Err(NoSolution);
         }
 
-        // Erase regions because we compute layouts in `rustc_transmute`,
-        // which will ICE for region vars.
-        let args = ecx.interner().erase_regions(goal.predicate.trait_ref.args);
-
-        let Some(assume) =
-            rustc_transmute::Assume::from_const(ecx.interner(), goal.param_env, args.const_at(2))
-        else {
-            return Err(NoSolution);
-        };
-
         // FIXME: This actually should destructure the `Result` we get from transmutability and
         // register candiates. We probably need to register >1 since we may have an OR of ANDs.
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             let certainty = ecx.is_transmutable(
-                rustc_transmute::Types { dst: args.type_at(0), src: args.type_at(1) },
-                assume,
+                goal.param_env,
+                goal.predicate.trait_ref.args.type_at(0),
+                goal.predicate.trait_ref.args.type_at(1),
+                goal.predicate.trait_ref.args.const_at(2),
             )?;
             ecx.evaluate_added_goals_and_make_canonical_response(certainty)
         })
@@ -651,9 +634,9 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     /// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
     /// ```
     fn consider_structural_builtin_unsize_candidates(
-        ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-        goal: Goal<'tcx, Self>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
+        ecx: &mut EvalCtxt<'_, Infcx>,
+        goal: Goal<I, Self>,
+    ) -> Vec<Candidate<I>> {
         if goal.predicate.polarity != ty::PredicatePolarity::Positive {
             return vec![];
         }
@@ -676,7 +659,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
             let goal = goal.with(ecx.interner(), (a_ty, b_ty));
             match (a_ty.kind(), b_ty.kind()) {
-                (ty::Infer(ty::TyVar(..)), ..) => bug!("unexpected infer {a_ty:?} {b_ty:?}"),
+                (ty::Infer(ty::TyVar(..)), ..) => panic!("unexpected infer {a_ty:?} {b_ty:?}"),
 
                 (_, ty::Infer(ty::TyVar(..))) => {
                     result_to_single(ecx.forced_ambiguity(MaybeCause::Ambiguity))
@@ -684,24 +667,24 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
 
                 // Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`.
                 (
-                    &ty::Dynamic(a_data, a_region, ty::Dyn),
-                    &ty::Dynamic(b_data, b_region, ty::Dyn),
+                    ty::Dynamic(a_data, a_region, ty::Dyn),
+                    ty::Dynamic(b_data, b_region, ty::Dyn),
                 ) => ecx.consider_builtin_dyn_upcast_candidates(
                     goal, a_data, a_region, b_data, b_region,
                 ),
 
                 // `T` -> `dyn Trait` unsizing.
-                (_, &ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
+                (_, ty::Dynamic(b_region, b_data, ty::Dyn)) => result_to_single(
                     ecx.consider_builtin_unsize_to_dyn_candidate(goal, b_region, b_data),
                 ),
 
                 // `[T; N]` -> `[T]` unsizing
-                (&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
+                (ty::Array(a_elem_ty, ..), ty::Slice(b_elem_ty)) => {
                     result_to_single(ecx.consider_builtin_array_unsize(goal, a_elem_ty, b_elem_ty))
                 }
 
                 // `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
-                (&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
+                (ty::Adt(a_def, a_args), ty::Adt(b_def, b_args))
                     if a_def.is_struct() && a_def == b_def =>
                 {
                     result_to_single(
@@ -710,7 +693,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
                 }
 
                 //  `(A, B, T)` -> `(A, B, U)` where `T: Unsize<U>`
-                (&ty::Tuple(a_tys), &ty::Tuple(b_tys))
+                (ty::Tuple(a_tys), ty::Tuple(b_tys))
                     if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
                 {
                     result_to_single(ecx.consider_builtin_tuple_unsize(goal, a_tys, b_tys))
@@ -722,7 +705,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
     }
 }
 
-impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
+impl<Infcx, I> EvalCtxt<'_, Infcx>
+where
+    Infcx: SolverDelegate<Interner = I>,
+    I: Interner,
+{
     /// Trait upcasting allows for coercions between trait objects:
     /// ```ignore (builtin impl example)
     /// trait Super {}
@@ -734,12 +721,12 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// ```
     fn consider_builtin_dyn_upcast_candidates(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        a_region: ty::Region<'tcx>,
-        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        b_region: ty::Region<'tcx>,
-    ) -> Vec<Candidate<TyCtxt<'tcx>>> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_data: I::BoundExistentialPredicates,
+        a_region: I::Region,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+    ) -> Vec<Candidate<I>> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _b_ty), .. } = goal;
 
@@ -757,7 +744,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 a_data.principal(),
             ));
         } else if let Some(a_principal) = a_data.principal() {
-            for new_a_principal in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)).skip(1) {
+            for new_a_principal in
+                Infcx::elaborate_supertraits(self.interner(), a_principal.with_self_ty(tcx, a_ty))
+                    .skip(1)
+            {
                 responses.extend(self.consider_builtin_upcast_to_principal(
                     goal,
                     CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
@@ -777,15 +767,15 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
     fn consider_builtin_unsize_to_dyn_candidate(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        b_region: ty::Region<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (a_ty, _), .. } = goal;
 
         // Can only unsize to an object-safe trait.
-        if b_data.principal_def_id().is_some_and(|def_id| !tcx.is_object_safe(def_id)) {
+        if b_data.principal_def_id().is_some_and(|def_id| !tcx.trait_is_object_safe(def_id)) {
             return Err(NoSolution);
         }
 
@@ -794,7 +784,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             // (i.e. the principal, all of the associated types match, and any auto traits)
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
-                b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
+                b_data.into_iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
             );
 
             // The type must be `Sized` to be unsized.
@@ -802,7 +792,11 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 GoalSource::ImplWhereBound,
                 goal.with(
                     tcx,
-                    ty::TraitRef::new(tcx, tcx.require_lang_item(LangItem::Sized, None), [a_ty]),
+                    ty::TraitRef::new(
+                        tcx,
+                        tcx.require_lang_item(TraitSolverLangItem::Sized),
+                        [a_ty],
+                    ),
                 ),
             );
 
@@ -814,24 +808,26 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
 
     fn consider_builtin_upcast_to_principal(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        source: CandidateSource<'tcx>,
-        a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        a_region: ty::Region<'tcx>,
-        b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
-        b_region: ty::Region<'tcx>,
-        upcast_principal: Option<ty::PolyExistentialTraitRef<'tcx>>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        source: CandidateSource<I>,
+        a_data: I::BoundExistentialPredicates,
+        a_region: I::Region,
+        b_data: I::BoundExistentialPredicates,
+        b_region: I::Region,
+        upcast_principal: Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>,
+    ) -> Result<Candidate<I>, NoSolution> {
         let param_env = goal.param_env;
 
         // We may upcast to auto traits that are either explicitly listed in
         // the object type's bounds, or implied by the principal trait ref's
         // supertraits.
-        let a_auto_traits: FxIndexSet<DefId> = a_data
+        let a_auto_traits: FxIndexSet<I::DefId> = a_data
             .auto_traits()
+            .into_iter()
             .chain(a_data.principal_def_id().into_iter().flat_map(|principal_def_id| {
                 self.interner()
                     .supertrait_def_ids(principal_def_id)
+                    .into_iter()
                     .filter(|def_id| self.interner().trait_is_auto(*def_id))
             }))
             .collect();
@@ -841,9 +837,9 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
         // having any inference side-effects. We process obligations because
         // unification may initially succeed due to deferred projection equality.
         let projection_may_match =
-            |ecx: &mut EvalCtxt<'_, InferCtxt<'tcx>>,
-             source_projection: ty::PolyExistentialProjection<'tcx>,
-             target_projection: ty::PolyExistentialProjection<'tcx>| {
+            |ecx: &mut EvalCtxt<'_, Infcx>,
+             source_projection: ty::Binder<I, ty::ExistentialProjection<I>>,
+             target_projection: ty::Binder<I, ty::ExistentialProjection<I>>| {
                 source_projection.item_def_id() == target_projection.item_def_id()
                     && ecx
                         .probe(|_| ProbeKind::UpcastProjectionCompatibility)
@@ -875,7 +871,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                     ty::ExistentialPredicate::Projection(target_projection) => {
                         let target_projection = bound.rebind(target_projection);
                         let mut matching_projections =
-                            a_data.projection_bounds().filter(|source_projection| {
+                            a_data.projection_bounds().into_iter().filter(|source_projection| {
                                 projection_may_match(ecx, *source_projection, target_projection)
                             });
                         let Some(source_projection) = matching_projections.next() else {
@@ -900,11 +896,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             // Also require that a_ty's lifetime outlives b_ty's lifetime.
             ecx.add_goal(
                 GoalSource::ImplWhereBound,
-                Goal::new(
-                    ecx.interner(),
-                    param_env,
-                    ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
-                ),
+                Goal::new(ecx.interner(), param_env, ty::OutlivesPredicate(a_region, b_region)),
             );
 
             ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
@@ -921,10 +913,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// `#[rustc_deny_explicit_impl]` in this case.
     fn consider_builtin_array_unsize(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        a_elem_ty: Ty<'tcx>,
-        b_elem_ty: Ty<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_elem_ty: I::Ty,
+        b_elem_ty: I::Ty,
+    ) -> Result<Candidate<I>, NoSolution> {
         self.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
         self.probe_builtin_trait_candidate(BuiltinImplSource::Misc)
             .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes))
@@ -945,26 +937,25 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// ```
     fn consider_builtin_struct_unsize(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        def: ty::AdtDef<'tcx>,
-        a_args: ty::GenericArgsRef<'tcx>,
-        b_args: ty::GenericArgsRef<'tcx>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        def: I::AdtDef,
+        a_args: I::GenericArgs,
+        b_args: I::GenericArgs,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
-        let unsizing_params = tcx.unsizing_params_for_adt(def.did());
+        let unsizing_params = tcx.unsizing_params_for_adt(def.def_id());
         // We must be unsizing some type parameters. This also implies
         // that the struct has a tail field.
         if unsizing_params.is_empty() {
             return Err(NoSolution);
         }
 
-        let tail_field = def.non_enum_variant().tail();
-        let tail_field_ty = tcx.type_of(tail_field.did);
+        let tail_field_ty = def.struct_tail_ty(tcx).unwrap();
 
-        let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
-        let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
+        let a_tail_ty = tail_field_ty.instantiate(tcx, &a_args);
+        let b_tail_ty = tail_field_ty.instantiate(tcx, &b_args);
 
         // Instantiate just the unsizing params from B into A. The type after
         // this instantiation must be equal to B. This is so we don't unsize
@@ -973,7 +964,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             a_args
                 .iter()
                 .enumerate()
-                .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { a }),
+                .map(|(i, a)| if unsizing_params.contains(i as u32) { b_args[i] } else { *a }),
         );
         let unsized_a_ty = Ty::new_adt(tcx, def, new_a_args);
 
@@ -986,7 +977,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Unsize, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_tail_ty, b_tail_ty],
                 ),
             ),
@@ -1007,10 +998,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// ```
     fn consider_builtin_tuple_unsize(
         &mut self,
-        goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
-        a_tys: &'tcx ty::List<Ty<'tcx>>,
-        b_tys: &'tcx ty::List<Ty<'tcx>>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+        goal: Goal<I, (I::Ty, I::Ty)>,
+        a_tys: I::Tys,
+        b_tys: I::Tys,
+    ) -> Result<Candidate<I>, NoSolution> {
         let tcx = self.interner();
         let Goal { predicate: (_a_ty, b_ty), .. } = goal;
 
@@ -1029,7 +1020,7 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
                 tcx,
                 ty::TraitRef::new(
                     tcx,
-                    tcx.require_lang_item(LangItem::Unsize, None),
+                    tcx.require_lang_item(TraitSolverLangItem::Unsize),
                     [a_last_ty, b_last_ty],
                 ),
             ),
@@ -1044,10 +1035,10 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     // the type's constituent types.
     fn disqualify_auto_trait_candidate_due_to_possible_impl(
         &mut self,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-    ) -> Option<Result<Candidate<TyCtxt<'tcx>>, NoSolution>> {
+        goal: Goal<I, TraitPredicate<I>>,
+    ) -> Option<Result<Candidate<I>, NoSolution>> {
         let self_ty = goal.predicate.self_ty();
-        match *self_ty.kind() {
+        match self_ty.kind() {
             // Stall int and float vars until they are resolved to a concrete
             // numerical type. That's because the check for impls below treats
             // int vars as matching any impl. Even if we filtered such impls,
@@ -1065,13 +1056,15 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
             | ty::Alias(ty::Projection | ty::Weak | ty::Inherent, ..)
             | ty::Placeholder(..) => Some(Err(NoSolution)),
 
-            ty::Infer(_) | ty::Bound(_, _) => bug!("unexpected type `{self_ty}`"),
+            ty::Infer(_) | ty::Bound(_, _) => panic!("unexpected type `{self_ty:?}`"),
 
             // Coroutines have one special built-in candidate, `Unpin`, which
             // takes precedence over the structural auto trait candidate being
             // assembled.
             ty::Coroutine(def_id, _)
-                if self.interner().is_lang_item(goal.predicate.def_id(), LangItem::Unpin) =>
+                if self
+                    .interner()
+                    .is_lang_item(goal.predicate.def_id(), TraitSolverLangItem::Unpin) =>
             {
                 match self.interner().coroutine_movability(def_id) {
                     Movability::Static => Some(Err(NoSolution)),
@@ -1144,13 +1137,13 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     /// wrapped in one.
     fn probe_and_evaluate_goal_for_constituent_tys(
         &mut self,
-        source: CandidateSource<'tcx>,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
+        source: CandidateSource<I>,
+        goal: Goal<I, TraitPredicate<I>>,
         constituent_tys: impl Fn(
-            &EvalCtxt<'_, InferCtxt<'tcx>>,
-            Ty<'tcx>,
-        ) -> Result<Vec<ty::Binder<'tcx, Ty<'tcx>>>, NoSolution>,
-    ) -> Result<Candidate<TyCtxt<'tcx>>, NoSolution> {
+            &EvalCtxt<'_, Infcx>,
+            I::Ty,
+        ) -> Result<Vec<ty::Binder<I, I::Ty>>, NoSolution>,
+    ) -> Result<Candidate<I>, NoSolution> {
         self.probe_trait_candidate(source).enter(|ecx| {
             ecx.add_goals(
                 GoalSource::ImplWhereBound,
@@ -1173,8 +1166,8 @@ impl<'tcx> EvalCtxt<'_, InferCtxt<'tcx>> {
     #[instrument(level = "trace", skip(self))]
     pub(super) fn compute_trait_goal(
         &mut self,
-        goal: Goal<'tcx, TraitPredicate<'tcx>>,
-    ) -> QueryResult<'tcx> {
+        goal: Goal<I, TraitPredicate<I>>,
+    ) -> QueryResult<I> {
         let candidates = self.assemble_and_evaluate_candidates(goal);
         self.merge_candidates(candidates)
     }
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 6c1fcbe06fc..7566a4d5066 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -3,7 +3,7 @@ use std::borrow::Cow;
 use rustc_ast::token::Token;
 use rustc_ast::{Path, Visibility};
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level,
+    codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
     SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -1052,7 +1052,7 @@ pub(crate) struct ExpectedIdentifier {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedIdentifier {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = Diag::new(
@@ -1112,7 +1112,7 @@ pub(crate) struct ExpectedSemi {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for ExpectedSemi {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let token_descr = TokenDescription::from_token(&self.token);
 
         let mut diag = Diag::new(
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 43f4963b27a..511805cf8d6 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -7,7 +7,7 @@ use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Delimiter, IdentIsRaw, Token, TokenKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
-use rustc_errors::{codes::*, Applicability, Diag, DiagCtxt, StashKey};
+use rustc_errors::{codes::*, Applicability, Diag, DiagCtxtHandle, StashKey};
 use rustc_lexer::unescape::{self, EscapeError, Mode};
 use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_lexer::{Cursor, LiteralKind};
@@ -113,8 +113,8 @@ struct StringReader<'psess, 'src> {
 }
 
 impl<'psess, 'src> StringReader<'psess, 'src> {
-    fn dcx(&self) -> &'psess DiagCtxt {
-        &self.psess.dcx
+    fn dcx(&self) -> DiagCtxtHandle<'psess> {
+        self.psess.dcx()
     }
 
     fn mk_sp(&self, lo: BytePos, hi: BytePos) -> Span {
@@ -248,8 +248,8 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
                     let suffix = if suffix_start < self.pos {
                         let string = self.str_from(suffix_start);
                         if string == "_" {
-                            self.psess
-                                .dcx
+                            self
+                                .dcx()
                                 .emit_err(errors::UnderscoreLiteralSuffix { span: self.mk_sp(suffix_start, self.pos) });
                             None
                         } else {
@@ -597,8 +597,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> {
     }
 
     fn report_non_started_raw_string(&self, start: BytePos, bad_char: char) -> ! {
-        self.psess
-            .dcx
+        self.dcx()
             .struct_span_fatal(
                 self.mk_sp(start, self.pos),
                 format!(
diff --git a/compiler/rustc_parse/src/lexer/tokentrees.rs b/compiler/rustc_parse/src/lexer/tokentrees.rs
index f7645446081..8e543454691 100644
--- a/compiler/rustc_parse/src/lexer/tokentrees.rs
+++ b/compiler/rustc_parse/src/lexer/tokentrees.rs
@@ -71,7 +71,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
 
     fn eof_err(&mut self) -> PErr<'psess> {
         let msg = "this file contains an unclosed delimiter";
-        let mut err = self.string_reader.psess.dcx.struct_span_err(self.token.span, msg);
+        let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg);
         for &(_, sp) in &self.diag_info.open_braces {
             err.span_label(sp, "unclosed delimiter");
             self.diag_info.unmatched_delims.push(UnmatchedDelim {
@@ -290,7 +290,7 @@ impl<'psess, 'src> TokenTreesReader<'psess, 'src> {
         // An unexpected closing delimiter (i.e., there is no matching opening delimiter).
         let token_str = token_to_string(&self.token);
         let msg = format!("unexpected closing delimiter: `{token_str}`");
-        let mut err = self.string_reader.psess.dcx.struct_span_err(self.token.span, msg);
+        let mut err = self.string_reader.dcx().struct_span_err(self.token.span, msg);
 
         report_suspicious_mismatch_block(
             &mut err,
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index cad25c66827..b7a790fcf83 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -3,7 +3,7 @@
 use std::iter::once;
 use std::ops::Range;
 
-use rustc_errors::{Applicability, DiagCtxt, ErrorGuaranteed};
+use rustc_errors::{Applicability, DiagCtxtHandle, ErrorGuaranteed};
 use rustc_lexer::unescape::{EscapeError, Mode};
 use rustc_span::{BytePos, Span};
 use tracing::debug;
@@ -11,7 +11,7 @@ use tracing::debug;
 use crate::errors::{MoreThanOneCharNote, MoreThanOneCharSugg, NoBraceUnicodeSub, UnescapeError};
 
 pub(crate) fn emit_unescape_error(
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     // interior part of the literal, between quotes
     lit: &str,
     // full span of the literal, including quotes and any prefix
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index 8eb299108d1..0a82ede3b75 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -351,7 +351,7 @@ pub(super) fn check_for_substitution(
 
     let Some((_, ascii_name, token)) = ASCII_ARRAY.iter().find(|&&(s, _, _)| s == ascii_str) else {
         let msg = format!("substitution character not found for '{ch}'");
-        reader.psess.dcx.span_bug(span, msg);
+        reader.dcx().span_bug(span, msg);
     };
 
     // special help suggestion for "directed" double quotes
diff --git a/compiler/rustc_parse/src/lib.rs b/compiler/rustc_parse/src/lib.rs
index 25cab7252a3..5522127be83 100644
--- a/compiler/rustc_parse/src/lib.rs
+++ b/compiler/rustc_parse/src/lib.rs
@@ -73,7 +73,7 @@ pub fn new_parser_from_file<'a>(
 ) -> Result<Parser<'a>, Vec<Diag<'a>>> {
     let source_file = psess.source_map().load_file(path).unwrap_or_else(|e| {
         let msg = format!("couldn't read {}: {}", path.display(), e);
-        let mut err = psess.dcx.struct_fatal(msg);
+        let mut err = psess.dcx().struct_fatal(msg);
         if let Some(sp) = sp {
             err.span(sp);
         }
@@ -115,7 +115,7 @@ fn source_file_to_stream<'psess>(
     override_span: Option<Span>,
 ) -> Result<TokenStream, Vec<Diag<'psess>>> {
     let src = source_file.src.as_ref().unwrap_or_else(|| {
-        psess.dcx.bug(format!(
+        psess.dcx().bug(format!(
             "cannot lex `source_file` without source: {}",
             psess.source_map().filename_for_diagnostics(&source_file.name)
         ));
@@ -179,7 +179,7 @@ pub fn parse_cfg_attr(
             }
         }
         _ => {
-            psess.dcx.emit_err(errors::MalformedCfgAttr {
+            psess.dcx().emit_err(errors::MalformedCfgAttr {
                 span: attr.span,
                 sugg: CFG_ATTR_GRAMMAR_HELP,
             });
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 62c8f9f5dac..f5c931034fd 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -41,7 +41,7 @@ impl AttrWrapper {
     }
 
     pub(crate) fn take_for_recovery(self, psess: &ParseSess) -> AttrVec {
-        psess.dcx.span_delayed_bug(
+        psess.dcx().span_delayed_bug(
             self.attrs.get(0).map(|attr| attr.span).unwrap_or(DUMMY_SP),
             "AttrVec is taken for recovery but no error is produced",
         );
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 2bb6fb53869..c1aac84bcae 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -1,7 +1,6 @@
 use super::pat::Expected;
 use super::{
-    BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep,
-    TokenExpectType, TokenType,
+    BlockMode, CommaRecoveryMode, Parser, PathStyle, Restrictions, SemiColonMode, SeqSep, TokenType,
 };
 use crate::errors::{
     AmbiguousPlus, AsyncMoveBlockIn2015, AttributeOnParamType, BadQPathStage2, BadTypePlus,
@@ -35,7 +34,7 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, Applicability, Diag, DiagCtxt, ErrorGuaranteed, FatalError, PErr, PResult,
+    pluralize, Applicability, Diag, DiagCtxtHandle, ErrorGuaranteed, FatalError, PErr, PResult,
     Subdiagnostic,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
@@ -241,8 +240,8 @@ impl<'a> DerefMut for SnapshotParser<'a> {
 }
 
 impl<'a> Parser<'a> {
-    pub fn dcx(&self) -> &'a DiagCtxt {
-        &self.psess.dcx
+    pub fn dcx(&self) -> DiagCtxtHandle<'a> {
+        self.psess.dcx()
     }
 
     /// Replace `self` with `snapshot.parser`.
@@ -667,7 +666,7 @@ impl<'a> Parser<'a> {
         {
             err.note("you may be trying to write a c-string literal");
             err.note("c-string literals require Rust 2021 or later");
-            err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new());
+            err.subdiagnostic(HelpUseLatestEdition::new());
         }
 
         // `pub` may be used for an item or `pub(crate)`
@@ -1045,9 +1044,7 @@ impl<'a> Parser<'a> {
     /// passes through any errors encountered. Used for error recovery.
     pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
         if let Err(err) =
-            self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| {
-                Ok(p.parse_token_tree())
-            })
+            self.parse_seq_to_before_tokens(kets, &[], SeqSep::none(), |p| Ok(p.parse_token_tree()))
         {
             err.cancel();
         }
@@ -2360,7 +2357,7 @@ impl<'a> Parser<'a> {
         let mut err = self.dcx().struct_span_err(span, msg);
         let sp = self.psess.source_map().start_point(self.token.span);
         if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
-            err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
         }
         err.span_label(span, "expected expression");
 
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index e15d6ab2123..5decfc142da 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -4,7 +4,7 @@ use super::pat::{CommaRecoveryMode, Expected, RecoverColon, RecoverComma};
 use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign};
 use super::{
     AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
-    SemiColonMode, SeqSep, TokenExpectType, TokenType, Trailing, TrailingToken,
+    SemiColonMode, SeqSep, TokenType, Trailing, TrailingToken,
 };
 
 use crate::errors;
@@ -1461,7 +1461,7 @@ impl<'a> Parser<'a> {
                     // If the input is something like `if a { 1 } else { 2 } | if a { 3 } else { 4 }`
                     // then suggest parens around the lhs.
                     if let Some(sp) = this.psess.ambiguous_block_expr_parse.borrow().get(&lo) {
-                        err.subdiagnostic(this.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+                        err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
                     }
                     err
                 })
@@ -2456,9 +2456,9 @@ impl<'a> Parser<'a> {
             self.expect(&token::BinOp(token::Or))?;
             let args = self
                 .parse_seq_to_before_tokens(
-                    &[&token::BinOp(token::Or), &token::OrOr],
+                    &[&token::BinOp(token::Or)],
+                    &[&token::OrOr],
                     SeqSep::trailing_allowed(token::Comma),
-                    TokenExpectType::NoExpect,
                     |p| p.parse_fn_block_param(),
                 )?
                 .0;
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 93a15c938ec..fde16ac957d 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -62,7 +62,7 @@ impl<'a> Parser<'a> {
                 let snapshot = self.create_snapshot_for_diagnostic();
                 match self.parse_ty() {
                     Ok(p) => {
-                        if let TyKind::ImplTrait(_, bounds, None) = &p.kind {
+                        if let TyKind::ImplTrait(_, bounds) = &p.kind {
                             let span = impl_span.to(self.token.span.shrink_to_lo());
                             let mut err = self.dcx().struct_span_err(
                                 span,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 3f5a4afdad8..42f8c6e38b9 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -633,7 +633,7 @@ impl<'a> Parser<'a> {
                     // This notably includes paths passed through `ty` macro fragments (#46438).
                     TyKind::Path(None, path) => path,
                     other => {
-                        if let TyKind::ImplTrait(_, bounds, None) = other
+                        if let TyKind::ImplTrait(_, bounds) = other
                             && let [bound] = bounds.as_slice()
                         {
                             // Suggest removing extra `impl` keyword:
@@ -1966,7 +1966,7 @@ impl<'a> Parser<'a> {
         if self.token.kind == token::Not {
             if let Err(mut err) = self.unexpected() {
                 // Encounter the macro invocation
-                err.subdiagnostic(self.dcx(), MacroExpandsToAdtField { adt_ty });
+                err.subdiagnostic(MacroExpandsToAdtField { adt_ty });
                 return Err(err);
             }
         }
@@ -2382,13 +2382,10 @@ impl<'a> Parser<'a> {
                             .into_iter()
                             .any(|s| self.prev_token.is_ident_named(s));
 
-                        err.subdiagnostic(
-                            self.dcx(),
-                            errors::FnTraitMissingParen {
-                                span: self.prev_token.span,
-                                machine_applicable,
-                            },
-                        );
+                        err.subdiagnostic(errors::FnTraitMissingParen {
+                            span: self.prev_token.span,
+                            machine_applicable,
+                        });
                     }
                     return Err(err);
                 }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index adf04fcf224..6dee913c141 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -335,18 +335,6 @@ impl TokenType {
     }
 }
 
-/// Used by [`Parser::expect_any_with_type`].
-#[derive(Copy, Clone, Debug)]
-enum TokenExpectType {
-    /// Unencountered tokens are inserted into [`Parser::expected_tokens`].
-    /// See [`Parser::check`].
-    Expect,
-
-    /// Unencountered tokens are not inserted into [`Parser::expected_tokens`].
-    /// See [`Parser::check_noexpect`].
-    NoExpect,
-}
-
 /// A sequence separator.
 #[derive(Debug)]
 struct SeqSep {
@@ -807,11 +795,13 @@ impl<'a> Parser<'a> {
     }
 
     /// Checks if the next token is contained within `kets`, and returns `true` if so.
-    fn expect_any_with_type(&mut self, kets: &[&TokenKind], expect: TokenExpectType) -> bool {
-        kets.iter().any(|k| match expect {
-            TokenExpectType::Expect => self.check(k),
-            TokenExpectType::NoExpect => self.check_noexpect(k),
-        })
+    fn expect_any_with_type(
+        &mut self,
+        kets_expected: &[&TokenKind],
+        kets_not_expected: &[&TokenKind],
+    ) -> bool {
+        kets_expected.iter().any(|k| self.check(k))
+            || kets_not_expected.iter().any(|k| self.check_noexpect(k))
     }
 
     /// Parses a sequence until the specified delimiters. The function
@@ -819,9 +809,9 @@ impl<'a> Parser<'a> {
     /// closing bracket.
     fn parse_seq_to_before_tokens<T>(
         &mut self,
-        kets: &[&TokenKind],
+        kets_expected: &[&TokenKind],
+        kets_not_expected: &[&TokenKind],
         sep: SeqSep,
-        expect: TokenExpectType,
         mut f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     ) -> PResult<'a, (ThinVec<T>, Trailing, Recovered)> {
         let mut first = true;
@@ -829,7 +819,7 @@ impl<'a> Parser<'a> {
         let mut trailing = Trailing::No;
         let mut v = ThinVec::new();
 
-        while !self.expect_any_with_type(kets, expect) {
+        while !self.expect_any_with_type(kets_expected, kets_not_expected) {
             if let token::CloseDelim(..) | token::Eof = self.token.kind {
                 break;
             }
@@ -927,7 +917,8 @@ impl<'a> Parser<'a> {
                                     if self.token == token::Colon {
                                         // we will try to recover in `maybe_recover_struct_lit_bad_delims`
                                         return Err(expect_err);
-                                    } else if let [token::CloseDelim(Delimiter::Parenthesis)] = kets
+                                    } else if let [token::CloseDelim(Delimiter::Parenthesis)] =
+                                        kets_expected
                                     {
                                         return Err(expect_err);
                                     } else {
@@ -940,7 +931,9 @@ impl<'a> Parser<'a> {
                     }
                 }
             }
-            if sep.trailing_sep_allowed && self.expect_any_with_type(kets, expect) {
+            if sep.trailing_sep_allowed
+                && self.expect_any_with_type(kets_expected, kets_not_expected)
+            {
                 trailing = Trailing::Yes;
                 break;
             }
@@ -1020,7 +1013,7 @@ impl<'a> Parser<'a> {
         sep: SeqSep,
         f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
     ) -> PResult<'a, (ThinVec<T>, Trailing, Recovered)> {
-        self.parse_seq_to_before_tokens(&[ket], sep, TokenExpectType::Expect, f)
+        self.parse_seq_to_before_tokens(&[ket], &[], sep, f)
     }
 
     /// Parses a sequence, including only the closing delimiter. The function
@@ -1603,7 +1596,7 @@ pub(crate) fn make_unclosed_delims_error(
     if let Some(sp) = unmatched.unclosed_span {
         spans.push(sp);
     };
-    let err = psess.dcx.create_err(MismatchedClosingDelimiter {
+    let err = psess.dcx().create_err(MismatchedClosingDelimiter {
         spans,
         delimiter: pprust::token_kind_to_string(&token::CloseDelim(found_delim)).to_string(),
         unmatched: unmatched.found_span,
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index 8af415f7c9d..f7f06ab7cbd 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -851,7 +851,7 @@ impl<'a> Parser<'a> {
 
         let sp = self.psess.source_map().start_point(self.token.span);
         if let Some(sp) = self.psess.ambiguous_block_expr_parse.borrow().get(&sp) {
-            err.subdiagnostic(self.dcx(), ExprParenthesesNeeded::surrounding(*sp));
+            err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
         }
 
         Err(err)
diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs
index 79a6cf1b541..3a4690670af 100644
--- a/compiler/rustc_parse/src/parser/tests.rs
+++ b/compiler/rustc_parse/src/parser/tests.rs
@@ -61,7 +61,7 @@ where
 {
     let mut p = string_to_parser(&psess, s);
     let x = f(&mut p).unwrap();
-    p.psess.dcx.abort_if_errors();
+    p.dcx().abort_if_errors();
     x
 }
 
@@ -193,7 +193,7 @@ impl<T: Write> Write for Shared<T> {
 #[allow(rustc::untranslatable_diagnostic)] // no translation needed for tests
 fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &str) {
     create_default_session_globals_then(|| {
-        let (handler, source_map, output) = create_test_handler();
+        let (dcx, source_map, output) = create_test_handler();
         source_map.new_source_file(Path::new("test.rs").to_owned().into(), file_text.to_owned());
 
         let primary_span = make_span(&file_text, &span_labels[0].start, &span_labels[0].end);
@@ -205,7 +205,7 @@ fn test_harness(file_text: &str, span_labels: Vec<SpanLabel>, expected_output: &
             println!("text: {:?}", source_map.span_to_snippet(span));
         }
 
-        handler.span_err(msp, "foo");
+        dcx.handle().span_err(msp, "foo");
 
         assert!(
             expected_output.chars().next() == Some('\n'),
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 5bed0317e5e..fcd623b477f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -9,7 +9,7 @@ use crate::errors::{
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Delimiter, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, Delimiter, Token, TokenKind};
 use rustc_ast::util::case::Case;
 use rustc_ast::{
     self as ast, BareFnTy, BoundAsyncness, BoundConstness, BoundPolarity, FnRetTy, GenericBound,
@@ -316,7 +316,7 @@ impl<'a> Parser<'a> {
                             TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
                         }
                         (TyKind::TraitObject(bounds, _), kw::Impl) => {
-                            TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, None)
+                            TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
                         }
                         _ => return Err(err),
                     };
@@ -670,33 +670,26 @@ impl<'a> Parser<'a> {
             })
         }
 
-        // 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
-        // for order, duplication, and whether they actually reference params.
-        let precise_capturing = if self.eat_keyword(kw::Use) {
-            let use_span = self.prev_token.span;
-            self.psess.gated_spans.gate(sym::precise_capturing, use_span);
-            let (args, args_span) = self.parse_precise_capturing_args()?;
-            Some(P((args, use_span.to(args_span))))
-        } else {
-            None
-        };
-
         // Always parse bounds greedily for better error recovery.
         let bounds = self.parse_generic_bounds()?;
 
         *impl_dyn_multi = bounds.len() > 1 || self.prev_token.kind == TokenKind::BinOp(token::Plus);
 
-        Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds, precise_capturing))
+        Ok(TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds))
     }
 
     fn parse_precise_capturing_args(
         &mut self,
     ) -> PResult<'a, (ThinVec<PreciseCapturingArg>, Span)> {
         let lo = self.token.span;
-        let (args, _) = self.parse_unspanned_seq(
-            &TokenKind::Lt,
-            &TokenKind::Gt,
+        self.expect_lt()?;
+        let (args, _, _) = self.parse_seq_to_before_tokens(
+            &[&TokenKind::Gt],
+            &[
+                &TokenKind::Ge,
+                &TokenKind::BinOp(BinOpToken::Shr),
+                &TokenKind::BinOpEq(BinOpToken::Shr),
+            ],
             SeqSep::trailing_allowed(token::Comma),
             |self_| {
                 if self_.check_keyword(kw::SelfUpper) {
@@ -717,6 +710,7 @@ impl<'a> Parser<'a> {
                 }
             },
         )?;
+        self.expect_gt()?;
         Ok((args, lo.to(self.prev_token.span)))
     }
 
@@ -828,6 +822,7 @@ impl<'a> Parser<'a> {
             || self.check(&token::OpenDelim(Delimiter::Parenthesis))
             || self.check_keyword(kw::Const)
             || self.check_keyword(kw::Async)
+            || self.check_keyword(kw::Use)
     }
 
     /// Parses a bound according to the grammar:
@@ -844,6 +839,14 @@ impl<'a> Parser<'a> {
         let bound = if self.token.is_lifetime() {
             self.error_lt_bound_with_modifiers(modifiers);
             self.parse_generic_lt_bound(lo, inner_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
+            // for order, duplication, and whether they actually reference params.
+            let use_span = self.prev_token.span;
+            self.psess.gated_spans.gate(sym::precise_capturing, use_span);
+            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)?
         };
@@ -1003,7 +1006,7 @@ impl<'a> Parser<'a> {
                             Applicability::MaybeIncorrect,
                         )
                     }
-                    TyKind::ImplTrait(_, bounds, None)
+                    TyKind::ImplTrait(_, bounds)
                         if let [GenericBound::Trait(tr, ..), ..] = bounds.as_slice() =>
                     {
                         (
diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs
index 19d6f512572..4ca52146039 100644
--- a/compiler/rustc_parse/src/validate_attr.rs
+++ b/compiler/rustc_parse/src/validate_attr.rs
@@ -65,7 +65,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
                     let res = match res {
                         Ok(lit) => {
                             if token_lit.suffix.is_some() {
-                                let mut err = psess.dcx.struct_span_err(
+                                let mut err = psess.dcx().struct_span_err(
                                     expr.span,
                                     "suffixed literals are not allowed in attributes",
                                 );
@@ -98,7 +98,7 @@ pub fn parse_meta<'a>(psess: &'a ParseSess, attr: &Attribute) -> PResult<'a, Met
                     //   the error because an earlier error will have already
                     //   been reported.
                     let msg = "attribute value must be a literal";
-                    let mut err = psess.dcx.struct_span_err(expr.span, msg);
+                    let mut err = psess.dcx().struct_span_err(expr.span, msg);
                     if let ast::ExprKind::Err(_) = expr.kind {
                         err.downgrade_to_delayed_bug();
                     }
@@ -114,7 +114,7 @@ fn check_meta_bad_delim(psess: &ParseSess, span: DelimSpan, delim: Delimiter) {
     if let Delimiter::Parenthesis = delim {
         return;
     }
-    psess.dcx.emit_err(errors::MetaBadDelim {
+    psess.dcx().emit_err(errors::MetaBadDelim {
         span: span.entire(),
         sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
     });
@@ -124,7 +124,7 @@ pub(super) fn check_cfg_attr_bad_delim(psess: &ParseSess, span: DelimSpan, delim
     if let Delimiter::Parenthesis = delim {
         return;
     }
-    psess.dcx.emit_err(errors::CfgAttrBadDelim {
+    psess.dcx().emit_err(errors::CfgAttrBadDelim {
         span: span.entire(),
         sugg: errors::MetaBadDelimSugg { open: span.open, close: span.close },
     });
@@ -191,7 +191,7 @@ fn emit_malformed_attribute(
     } else {
         suggestions.sort();
         psess
-            .dcx
+            .dcx()
             .struct_span_err(span, error_msg)
             .with_span_suggestions(
                 span,
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 6ce7c41acc8..a0b3470df6d 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -8,8 +8,8 @@ use crate::{errors, fluent_generated as fluent};
 use rustc_ast::{ast, AttrKind, AttrStyle, Attribute, LitKind};
 use rustc_ast::{MetaItemKind, MetaItemLit, NestedMetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::StashKey;
-use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan};
+use rustc_errors::{Applicability, IntoDiagArg, MultiSpan};
+use rustc_errors::{DiagCtxtHandle, StashKey};
 use rustc_feature::{
     is_unsafe_attr, AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP,
 };
@@ -99,7 +99,7 @@ struct CheckAttrVisitor<'tcx> {
 }
 
 impl<'tcx> CheckAttrVisitor<'tcx> {
-    fn dcx(&self) -> &'tcx DiagCtxt {
+    fn dcx(&self) -> DiagCtxtHandle<'tcx> {
         self.tcx.dcx()
     }
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index a935f1ad7d3..25df80d5a92 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -6,8 +6,8 @@ use std::{
 use crate::fluent_generated as fluent;
 use rustc_ast::{ast, Label};
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxt, DiagSymbolList, Diagnostic, EmissionGuarantee, Level,
-    MultiSpan, SubdiagMessageOp, Subdiagnostic,
+    codes::*, Applicability, Diag, DiagCtxtHandle, DiagSymbolList, Diagnostic, EmissionGuarantee,
+    Level, MultiSpan, SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -880,7 +880,7 @@ pub struct ItemFollowingInnerAttr {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for InvalidAttrAtCrateLevel {
     #[track_caller]
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::passes_invalid_attr_at_crate_level);
         diag.span(self.span);
         diag.arg("name", self.name);
@@ -1030,7 +1030,7 @@ pub struct BreakNonLoop<'a> {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'_, G> for BreakNonLoop<'a> {
     #[track_caller]
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::passes_break_non_loop);
         diag.span(self.span);
         diag.code(E0571);
@@ -1176,7 +1176,7 @@ pub struct NakedFunctionsAsmBlock {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for NakedFunctionsAsmBlock {
     #[track_caller]
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::passes_naked_functions_asm_block);
         diag.span(self.span);
         diag.code(E0787);
@@ -1264,7 +1264,7 @@ pub struct NoMainErr {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for NoMainErr {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         let mut diag = Diag::new(dcx, level, fluent::passes_no_main_function);
         diag.span(DUMMY_SP);
         diag.code(E0601);
@@ -1322,7 +1322,7 @@ pub struct DuplicateLangItem {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for DuplicateLangItem {
     #[track_caller]
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(
             dcx,
             level,
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index d49298781a2..0ba61f8e8b4 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -429,7 +429,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_param_bound(&mut self, b: &'v hir::GenericBound<'v>) {
         record_variants!(
             (self, b, b, Id::None, hir, GenericBound, GenericBound),
-            [Trait, Outlives]
+            [Trait, Outlives, Use]
         );
         hir_visit::walk_param_bound(self, b)
     }
@@ -659,7 +659,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_param_bound(&mut self, b: &'v ast::GenericBound, _ctxt: BoundKind) {
         record_variants!(
             (self, b, b, Id::None, ast, GenericBound, GenericBound),
-            [Trait, Outlives]
+            [Trait, Outlives, Use]
         );
         ast_visit::walk_param_bound(self, b)
     }
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 248a741af90..3f44b11850e 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError;
 use crate::query::DepKind;
 use crate::query::{QueryContext, QueryStackFrame};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{Diag, DiagCtxt};
+use rustc_errors::{Diag, DiagCtxtHandle};
 use rustc_hir::def::DefKind;
 use rustc_session::Session;
 use rustc_span::Span;
@@ -600,7 +600,7 @@ pub fn report_cycle<'a>(
 pub fn print_query_stack<Qcx: QueryContext>(
     qcx: Qcx,
     mut current_query: Option<QueryJobId>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
     num_frames: Option<usize>,
     mut file: Option<std::fs::File>,
 ) -> usize {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 8d470c6c61e..263daa11ec3 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -6,7 +6,7 @@ use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    codes::*, report_ambiguity_error, struct_span_code_err, Applicability, Diag, DiagCtxt,
+    codes::*, report_ambiguity_error, struct_span_code_err, Applicability, Diag, DiagCtxtHandle,
     ErrorGuaranteed, MultiSpan, SuggestionStyle,
 };
 use rustc_feature::BUILTIN_ATTRIBUTES;
@@ -120,7 +120,7 @@ fn reduce_impl_span_to_impl_keyword(sm: &SourceMap, impl_span: Span) -> Span {
 }
 
 impl<'a, 'tcx> Resolver<'a, 'tcx> {
-    pub(crate) fn dcx(&self) -> &'tcx DiagCtxt {
+    pub(crate) fn dcx(&self) -> DiagCtxtHandle<'tcx> {
         self.tcx.dcx()
     }
 
@@ -334,12 +334,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             Some((import, _, true)) if should_remove_import && !import.is_glob() => {
                 // Simple case - remove the entire import. Due to the above match arm, this can
                 // only be a single use so just remove it entirely.
-                err.subdiagnostic(
-                    self.tcx.dcx(),
-                    errors::ToolOnlyRemoveUnnecessaryImport {
-                        span: import.use_span_with_attributes,
-                    },
-                );
+                err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport {
+                    span: import.use_span_with_attributes,
+                });
             }
             Some((import, span, _)) => {
                 self.add_suggestion_for_rename_of_use(&mut err, name, import, span);
@@ -405,12 +402,9 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         }
 
         if let Some(suggestion) = suggestion {
-            err.subdiagnostic(
-                self.dcx(),
-                ChangeImportBindingSuggestion { span: binding_span, suggestion },
-            );
+            err.subdiagnostic(ChangeImportBindingSuggestion { span: binding_span, suggestion });
         } else {
-            err.subdiagnostic(self.dcx(), ChangeImportBinding { span: binding_span });
+            err.subdiagnostic(ChangeImportBinding { span: binding_span });
         }
     }
 
@@ -458,20 +452,19 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         // previous imports.
         if found_closing_brace {
             if let Some(span) = extend_span_to_previous_binding(self.tcx.sess, span) {
-                err.subdiagnostic(self.dcx(), errors::ToolOnlyRemoveUnnecessaryImport { span });
+                err.subdiagnostic(errors::ToolOnlyRemoveUnnecessaryImport { span });
             } else {
                 // Remove the entire line if we cannot extend the span back, this indicates an
                 // `issue_52891::{self}` case.
-                err.subdiagnostic(
-                    self.dcx(),
-                    errors::RemoveUnnecessaryImport { span: import.use_span_with_attributes },
-                );
+                err.subdiagnostic(errors::RemoveUnnecessaryImport {
+                    span: import.use_span_with_attributes,
+                });
             }
 
             return;
         }
 
-        err.subdiagnostic(self.dcx(), errors::RemoveUnnecessaryImport { span });
+        err.subdiagnostic(errors::RemoveUnnecessaryImport { span });
     }
 
     pub(crate) fn lint_if_path_starts_with_module(
@@ -682,10 +675,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     .dcx()
                     .create_err(errors::VariableIsNotBoundInAllPatterns { multispan: msp, name });
                 for sp in target_sp {
-                    err.subdiagnostic(self.dcx(), errors::PatternDoesntBindName { span: sp, name });
+                    err.subdiagnostic(errors::PatternDoesntBindName { span: sp, name });
                 }
                 for sp in origin_sp {
-                    err.subdiagnostic(self.dcx(), errors::VariableNotInAllPatterns { span: sp });
+                    err.subdiagnostic(errors::VariableNotInAllPatterns { span: sp });
                 }
                 if could_be_path {
                     let import_suggestions = self.lookup_import_candidates(
@@ -1446,12 +1439,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
         );
 
         if macro_kind == MacroKind::Bang && ident.name == sym::macro_rules {
-            err.subdiagnostic(self.dcx(), MaybeMissingMacroRulesName { span: ident.span });
+            err.subdiagnostic(MaybeMissingMacroRulesName { span: ident.span });
             return;
         }
 
         if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
-            err.subdiagnostic(self.dcx(), ExplicitUnsafeTraits { span: ident.span, ident });
+            err.subdiagnostic(ExplicitUnsafeTraits { span: ident.span, ident });
             return;
         }
 
@@ -1467,14 +1460,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let scope = self.local_macro_def_scopes[&def_id];
             let parent_nearest = parent_scope.module.nearest_parent_mod();
             if Some(parent_nearest) == scope.opt_def_id() {
-                err.subdiagnostic(self.dcx(), MacroDefinedLater { span: unused_ident.span });
-                err.subdiagnostic(self.dcx(), MacroSuggMovePosition { span: ident.span, ident });
+                err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });
+                err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });
                 return;
             }
         }
 
         if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
-            err.subdiagnostic(self.dcx(), AddedMacroUse);
+            err.subdiagnostic(AddedMacroUse);
             return;
         }
 
@@ -1484,13 +1477,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             let span = self.def_span(def_id);
             let source_map = self.tcx.sess.source_map();
             let head_span = source_map.guess_head_span(span);
-            err.subdiagnostic(
-                self.dcx(),
-                ConsiderAddingADerive {
-                    span: head_span.shrink_to_lo(),
-                    suggestion: "#[derive(Default)]\n".to_string(),
-                },
-            );
+            err.subdiagnostic(ConsiderAddingADerive {
+                span: head_span.shrink_to_lo(),
+                suggestion: "#[derive(Default)]\n".to_string(),
+            });
         }
         for ns in [Namespace::MacroNS, Namespace::TypeNS, Namespace::ValueNS] {
             if let Ok(binding) = self.early_resolve_ident_in_lexical_scope(
@@ -1533,7 +1523,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             imported_ident: ident,
                             imported_ident_desc: &desc,
                         };
-                        err.subdiagnostic(self.tcx.dcx(), note);
+                        err.subdiagnostic(note);
                         // Silence the 'unused import' warning we might get,
                         // since this diagnostic already covers that import.
                         self.record_use(ident, binding, Used::Other);
@@ -1544,7 +1534,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     imported_ident: ident,
                     imported_ident_desc: &desc,
                 };
-                err.subdiagnostic(self.tcx.dcx(), note);
+                err.subdiagnostic(note);
                 return;
             }
         }
@@ -1599,7 +1589,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 }
             };
             did_label_def_span = true;
-            err.subdiagnostic(self.tcx.dcx(), label);
+            err.subdiagnostic(label);
         }
 
         let (span, msg, sugg) = if let SuggestionTarget::SimilarlyNamed = suggestion.target
@@ -1790,7 +1780,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     outer_ident_descr: this_res.descr(),
                     outer_ident,
                 };
-                err.subdiagnostic(self.tcx.dcx(), label);
+                err.subdiagnostic(label);
             }
         }
 
@@ -1805,14 +1795,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             non_exhaustive = Some(attr.span);
         } else if let Some(span) = ctor_fields_span {
             let label = errors::ConstructorPrivateIfAnyFieldPrivate { span };
-            err.subdiagnostic(self.tcx.dcx(), label);
+            err.subdiagnostic(label);
             if let Res::Def(_, d) = res
                 && let Some(fields) = self.field_visibility_spans.get(&d)
             {
                 let spans = fields.iter().map(|span| *span).collect();
                 let sugg =
                     errors::ConsiderMakingTheFieldPublic { spans, number_of_fields: fields.len() };
-                err.subdiagnostic(self.tcx.dcx(), sugg);
+                err.subdiagnostic(sugg);
             }
         }
 
@@ -1921,7 +1911,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                 first,
                 dots: next_binding.is_some(),
             };
-            err.subdiagnostic(self.tcx.dcx(), note);
+            err.subdiagnostic(note);
         }
         // We prioritize shorter paths, non-core imports and direct imports over the alternatives.
         sugg_paths.sort_by_key(|(p, reexport)| (p.len(), p[0] == "core", *reexport));
@@ -1940,7 +1930,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             } else {
                 errors::ImportIdent::Directly { span: dedup_span, ident, path }
             };
-            err.subdiagnostic(self.tcx.dcx(), sugg);
+            err.subdiagnostic(sugg);
             break;
         }
 
@@ -2521,14 +2511,14 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             }
 
             let note = errors::FoundItemConfigureOut { span: name.span };
-            err.subdiagnostic(self.tcx.dcx(), note);
+            err.subdiagnostic(note);
 
             if let MetaItemKind::List(nested) = &cfg.kind
                 && let NestedMetaItem::MetaItem(meta_item) = &nested[0]
                 && let MetaItemKind::NameValue(feature_name) = &meta_item.kind
             {
                 let note = errors::ItemWasBehindFeature { feature: feature_name.symbol };
-                err.subdiagnostic(self.tcx.dcx(), note);
+                err.subdiagnostic(note);
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 6c7afb305ba..96a4647b942 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1294,12 +1294,12 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                             // exclude decl_macro
                             if self.get_macro_by_def_id(def_id).macro_rules =>
                         {
-                            err.subdiagnostic(self.dcx(), ConsiderAddingMacroExport {
+                            err.subdiagnostic( ConsiderAddingMacroExport {
                                 span: binding.span,
                             });
                         }
                         _ => {
-                            err.subdiagnostic(self.dcx(), ConsiderMarkingAsPub {
+                            err.subdiagnostic( ConsiderMarkingAsPub {
                                 span: import.span,
                                 ident,
                             });
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index c1e83c59f98..5ab6ba23a7d 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -799,7 +799,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
                 self.r.record_partial_res(ty.id, PartialRes::new(res));
                 visit::walk_ty(self, ty)
             }
-            TyKind::ImplTrait(node_id, _, _) => {
+            TyKind::ImplTrait(node_id, _) => {
                 let candidates = self.lifetime_elision_candidates.take();
                 visit::walk_ty(self, ty);
                 self.record_lifetime_params_for_impl_trait(*node_id);
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index be24755d4c5..764cc350182 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -829,7 +829,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                         auto-traits; structs and enums can't be bound in that way",
                 );
                 if bounds.iter().all(|bound| match bound {
-                    ast::GenericBound::Outlives(_) => true,
+                    ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..) => true,
                     ast::GenericBound::Trait(tr, _) => tr.span == base_error.span,
                 }) {
                     let mut sugg = vec![];
@@ -1109,14 +1109,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                 Side::Start => (segment.ident.span.between(range.span), " @ ".into()),
                 Side::End => (range.span.to(segment.ident.span), format!("{} @ ..", segment.ident)),
             };
-            err.subdiagnostic(
-                self.r.dcx(),
-                errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {
-                    span,
-                    ident: segment.ident,
-                    snippet,
-                },
-            );
+            err.subdiagnostic(errors::UnexpectedResUseAtOpInSlicePatWithRangeSugg {
+                span,
+                ident: segment.ident,
+                snippet,
+            });
         }
 
         enum Side {
@@ -1208,13 +1205,10 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         });
 
         if let Some(param) = param {
-            err.subdiagnostic(
-                self.r.dcx(),
-                errors::UnexpectedResChangeTyToConstParamSugg {
-                    span: param.shrink_to_lo(),
-                    applicability,
-                },
-            );
+            err.subdiagnostic(errors::UnexpectedResChangeTyToConstParamSugg {
+                span: param.shrink_to_lo(),
+                applicability,
+            });
         }
     }
 
@@ -3210,7 +3204,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
                                 .inputs
                                 .iter()
                                 .filter_map(|param| match &param.ty.kind {
-                                    TyKind::ImplTrait(_, bounds, _) => Some(bounds),
+                                    TyKind::ImplTrait(_, bounds) => Some(bounds),
                                     _ => None,
                                 })
                                 .flat_map(|bounds| bounds.into_iter())
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index a622f1b577d..5f9c3a14d60 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -149,7 +149,14 @@ pub enum InstrumentCoverage {
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
 pub struct CoverageOptions {
     pub level: CoverageLevel,
-    // Other boolean or enum-valued options might be added here.
+
+    /// `-Z coverage-options=no-mir-spans`: Don't extract block coverage spans
+    /// from MIR statements/terminators, making it easier to inspect/debug
+    /// branch and MC/DC coverage mappings.
+    ///
+    /// For internal debugging only. If other code changes would make it hard
+    /// to keep supporting this flag, remove it.
+    pub no_mir_spans: bool,
 }
 
 /// Controls whether branch coverage or MC/DC coverage is enabled.
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
index dce56382a53..4cbc1b57022 100644
--- a/compiler/rustc_session/src/errors.rs
+++ b/compiler/rustc_session/src/errors.rs
@@ -3,8 +3,8 @@ use std::num::NonZero;
 use rustc_ast::token;
 use rustc_ast::util::literal::LitError;
 use rustc_errors::{
-    codes::*, Diag, DiagCtxt, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed, Level,
-    MultiSpan,
+    codes::*, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, EmissionGuarantee, ErrorGuaranteed,
+    Level, MultiSpan,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{Span, Symbol};
@@ -19,7 +19,7 @@ pub(crate) struct FeatureGateError {
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError {
     #[track_caller]
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         Diag::new(dcx, level, self.explain).with_span(self.span).with_code(E0658)
     }
 }
@@ -401,7 +401,7 @@ pub fn report_lit_error(
         valid.then(|| format!("0{}{}", base_char.to_ascii_lowercase(), &suffix[1..]))
     }
 
-    let dcx = &psess.dcx;
+    let dcx = psess.dcx();
     match err {
         LitError::InvalidSuffix(suffix) => {
             dcx.emit_err(InvalidLiteralSuffix { span, kind: lit.kind.descr(), suffix })
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index fd4a3a9e6ce..145af50117c 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -395,7 +395,8 @@ mod desc {
     pub const parse_optimization_fuel: &str = "crate=integer";
     pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
     pub const parse_instrument_coverage: &str = parse_bool;
-    pub const parse_coverage_options: &str = "`block` | `branch` | `condition` | `mcdc`";
+    pub const parse_coverage_options: &str =
+        "`block` | `branch` | `condition` | `mcdc` | `no-mir-spans`";
     pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
     pub const parse_unpretty: &str = "`string` or `string=string`";
     pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
@@ -963,6 +964,7 @@ mod parse {
                 "branch" => slot.level = CoverageLevel::Branch,
                 "condition" => slot.level = CoverageLevel::Condition,
                 "mcdc" => slot.level = CoverageLevel::Mcdc,
+                "no-mir-spans" => slot.no_mir_spans = true,
                 _ => return false,
             }
         }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index df07f81bc45..200505aaea2 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -15,8 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
 use rustc_errors::emitter::{stderr_destination, HumanEmitter, SilentEmitter};
 use rustc_errors::{
-    fallback_fluent_bundle, ColorConfig, Diag, DiagCtxt, DiagMessage, EmissionGuarantee, MultiSpan,
-    StashKey,
+    fallback_fluent_bundle, ColorConfig, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage,
+    EmissionGuarantee, MultiSpan, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -106,12 +106,12 @@ pub fn feature_err_issue(
 
     // Cancel an earlier warning for this same error, if it exists.
     if let Some(span) = span.primary_span() {
-        if let Some(err) = sess.psess.dcx.steal_non_err(span, StashKey::EarlySyntaxWarning) {
+        if let Some(err) = sess.dcx().steal_non_err(span, StashKey::EarlySyntaxWarning) {
             err.cancel()
         }
     }
 
-    let mut err = sess.psess.dcx.create_err(FeatureGateError { span, explain: explain.into() });
+    let mut err = sess.dcx().create_err(FeatureGateError { span, explain: explain.into() });
     add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
     err
 }
@@ -140,7 +140,7 @@ pub fn feature_warn_issue(
     issue: GateIssue,
     explain: &'static str,
 ) {
-    let mut err = sess.psess.dcx.struct_span_warn(span, explain);
+    let mut err = sess.dcx().struct_span_warn(span, explain);
     add_feature_diagnostics_for_issue(&mut err, sess, feature, issue, false, None);
 
     // Decorate this as a future-incompatibility lint as in rustc_middle::lint::lint_level
@@ -178,30 +178,30 @@ pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
     inject_span: Option<Span>,
 ) {
     if let Some(n) = find_feature_issue(feature, issue) {
-        err.subdiagnostic(sess.dcx(), FeatureDiagnosticForIssue { n });
+        err.subdiagnostic(FeatureDiagnosticForIssue { n });
     }
 
     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
     if sess.psess.unstable_features.is_nightly_build() {
         if feature_from_cli {
-            err.subdiagnostic(sess.dcx(), CliFeatureDiagnosticHelp { feature });
+            err.subdiagnostic(CliFeatureDiagnosticHelp { feature });
         } else if let Some(span) = inject_span {
-            err.subdiagnostic(sess.dcx(), FeatureDiagnosticSuggestion { feature, span });
+            err.subdiagnostic(FeatureDiagnosticSuggestion { feature, span });
         } else {
-            err.subdiagnostic(sess.dcx(), FeatureDiagnosticHelp { feature });
+            err.subdiagnostic(FeatureDiagnosticHelp { feature });
         }
 
         if sess.opts.unstable_opts.ui_testing {
-            err.subdiagnostic(sess.dcx(), SuggestUpgradeCompiler::ui_testing());
+            err.subdiagnostic(SuggestUpgradeCompiler::ui_testing());
         } else if let Some(suggestion) = SuggestUpgradeCompiler::new() {
-            err.subdiagnostic(sess.dcx(), suggestion);
+            err.subdiagnostic(suggestion);
         }
     }
 }
 
 /// Info about a parsing session.
 pub struct ParseSess {
-    pub dcx: DiagCtxt,
+    dcx: DiagCtxt,
     pub unstable_features: UnstableFeatures,
     pub config: Cfg,
     pub check_config: CheckCfg,
@@ -326,4 +326,8 @@ impl ParseSess {
         // AppendOnlyVec, so we resort to this scheme.
         self.proc_macro_quoted_spans.iter_enumerated()
     }
+
+    pub fn dcx(&self) -> DiagCtxtHandle<'_> {
+        self.dcx.handle()
+    }
 }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 87bbfcf07c8..89d029fa6e0 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -22,8 +22,8 @@ use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter, HumanR
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
-    codes::*, fallback_fluent_bundle, Diag, DiagCtxt, DiagMessage, Diagnostic, ErrorGuaranteed,
-    FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl,
+    codes::*, fallback_fluent_bundle, Diag, DiagCtxt, DiagCtxtHandle, DiagMessage, Diagnostic,
+    ErrorGuaranteed, FatalAbort, FluentBundle, LazyFallbackBundle, TerminalUrl,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -328,8 +328,8 @@ impl Session {
     }
 
     #[inline]
-    pub fn dcx(&self) -> &DiagCtxt {
-        &self.psess.dcx
+    pub fn dcx(&self) -> DiagCtxtHandle<'_> {
+        self.psess.dcx()
     }
 
     #[inline]
@@ -363,6 +363,11 @@ impl Session {
             && self.opts.unstable_opts.coverage_options.level >= CoverageLevel::Mcdc
     }
 
+    /// True if `-Zcoverage-options=no-mir-spans` was passed.
+    pub fn coverage_no_mir_spans(&self) -> bool {
+        self.opts.unstable_opts.coverage_options.no_mir_spans
+    }
+
     pub fn is_sanitizer_cfi_enabled(&self) -> bool {
         self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
     }
@@ -1065,7 +1070,7 @@ pub fn build_session(
         match profiler {
             Ok(profiler) => Some(Arc::new(profiler)),
             Err(e) => {
-                dcx.emit_warn(errors::FailedToCreateProfiler { err: e.to_string() });
+                dcx.handle().emit_warn(errors::FailedToCreateProfiler { err: e.to_string() });
                 None
             }
         }
@@ -1366,7 +1371,7 @@ impl EarlyDiagCtxt {
     /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
     /// previous dcx will be emitted.
     pub fn abort_if_error_and_set_error_format(&mut self, output: ErrorOutputType) {
-        self.dcx.abort_if_errors();
+        self.dcx.handle().abort_if_errors();
 
         let emitter = mk_emitter(output);
         self.dcx = DiagCtxt::new(emitter);
@@ -1375,44 +1380,44 @@ impl EarlyDiagCtxt {
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_note(&self, msg: impl Into<DiagMessage>) {
-        self.dcx.note(msg)
+        self.dcx.handle().note(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_help(&self, msg: impl Into<DiagMessage>) {
-        self.dcx.struct_help(msg).emit()
+        self.dcx.handle().struct_help(msg).emit()
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     #[must_use = "ErrorGuaranteed must be returned from `run_compiler` in order to exit with a non-zero status code"]
     pub fn early_err(&self, msg: impl Into<DiagMessage>) -> ErrorGuaranteed {
-        self.dcx.err(msg)
+        self.dcx.handle().err(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_fatal(&self, msg: impl Into<DiagMessage>) -> ! {
-        self.dcx.fatal(msg)
+        self.dcx.handle().fatal(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_struct_fatal(&self, msg: impl Into<DiagMessage>) -> Diag<'_, FatalAbort> {
-        self.dcx.struct_fatal(msg)
+        self.dcx.handle().struct_fatal(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_warn(&self, msg: impl Into<DiagMessage>) {
-        self.dcx.warn(msg)
+        self.dcx.handle().warn(msg)
     }
 
     #[allow(rustc::untranslatable_diagnostic)]
     #[allow(rustc::diagnostic_outside_of_impl)]
     pub fn early_struct_warn(&self, msg: impl Into<DiagMessage>) -> Diag<'_, ()> {
-        self.dcx.struct_warn(msg)
+        self.dcx.handle().struct_warn(msg)
     }
 }
 
diff --git a/compiler/rustc_span/src/span_encoding.rs b/compiler/rustc_span/src/span_encoding.rs
index 31d2a9db01d..116056a69c1 100644
--- a/compiler/rustc_span/src/span_encoding.rs
+++ b/compiler/rustc_span/src/span_encoding.rs
@@ -327,43 +327,24 @@ impl Span {
     // interner and can fall back to `Span::new`.
     #[inline]
     pub fn map_ctxt(self, update: impl FnOnce(SyntaxContext) -> SyntaxContext) -> Span {
-        let (updated_ctxt32, data);
         match_span_kind! {
             self,
             InlineCtxt(span) => {
-                updated_ctxt32 = update(SyntaxContext::from_u16(span.ctxt)).as_u32();
+                let updated_ctxt32 = update(SyntaxContext::from_u16(span.ctxt)).as_u32();
                 // Any small new context including zero will preserve the format.
-                if updated_ctxt32 <= MAX_CTXT {
-                    return InlineCtxt::span(span.lo, span.len, updated_ctxt32 as u16);
-                }
-                data = span.data();
-            },
-            InlineParent(span) => {
-                updated_ctxt32 = update(SyntaxContext::root()).as_u32();
-                // Only if the new context is zero the format will be preserved.
-                if updated_ctxt32 == 0 {
-                    // Do nothing.
-                    return self;
-                }
-                data = span.data();
-            },
-            PartiallyInterned(span) => {
-                updated_ctxt32 = update(SyntaxContext::from_u16(span.ctxt)).as_u32();
-                // Any small new context excluding zero will preserve the format.
-                // Zero may change the format to `InlineParent` if parent and len are small enough.
-                if updated_ctxt32 <= MAX_CTXT && updated_ctxt32 != 0 {
-                    return PartiallyInterned::span(span.index, updated_ctxt32 as u16);
-                }
-                data = span.data();
-            },
-            Interned(span) => {
-                data = span.data();
-                updated_ctxt32 = update(data.ctxt).as_u32();
+                return if updated_ctxt32 <= MAX_CTXT {
+                    InlineCtxt::span(span.lo, span.len, updated_ctxt32 as u16)
+                } else {
+                    span.data().with_ctxt(SyntaxContext::from_u32(updated_ctxt32))
+                };
             },
+            InlineParent(_span) => {},
+            PartiallyInterned(_span) => {},
+            Interned(_span) => {},
         }
 
-        // We could not keep the span in the same inline format, fall back to the complete logic.
-        data.with_ctxt(SyntaxContext::from_u32(updated_ctxt32))
+        let data = self.data_untracked();
+        data.with_ctxt(update(data.ctxt))
     }
 
     // Returns either syntactic context, if it can be retrieved without taking the interner lock,
diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs
index b5721ee489d..9b87a1419fa 100644
--- a/compiler/rustc_symbol_mangling/src/errors.rs
+++ b/compiler/rustc_symbol_mangling/src/errors.rs
@@ -1,6 +1,6 @@
 //! Errors emitted by symbol_mangling.
 
-use rustc_errors::{Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level};
+use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level};
 use rustc_span::Span;
 use std::fmt;
 
@@ -14,7 +14,7 @@ pub struct TestOutput {
 // natural language, and (b) it's only used in tests. So we construct it
 // manually and avoid the fluent machinery.
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for TestOutput {
-    fn into_diag(self, dcx: &'_ DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let TestOutput { span, kind, content } = self;
 
         #[allow(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index b442446f79b..a46cba35b2d 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,6 +1,6 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, Applicability, Diag, DiagCtxt, Diagnostic, EmissionGuarantee, Level,
+    codes::*, Applicability, Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
     SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -59,7 +59,7 @@ pub struct NegativePositiveConflict<'tcx> {
 
 impl<G: EmissionGuarantee> Diagnostic<'_, G> for NegativePositiveConflict<'_> {
     #[track_caller]
-    fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
         let mut diag = Diag::new(dcx, level, fluent::trait_selection_negative_positive_conflict);
         diag.arg("trait_desc", self.trait_desc.print_only_trait_path().to_string());
         diag.arg("self_desc", self.self_ty.map_or_else(|| "none".to_string(), |ty| ty.to_string()));
diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs
new file mode 100644
index 00000000000..a7c8cc5a32b
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve.rs
@@ -0,0 +1,12 @@
+pub use rustc_next_trait_solver::solve::*;
+
+mod fulfill;
+mod infcx;
+pub mod inspect;
+mod normalize;
+mod select;
+
+pub use fulfill::{FulfillmentCtxt, NextSolverError};
+pub(crate) use normalize::deeply_normalize_for_diagnostics;
+pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
+pub use select::InferCtxtSelectExt;
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 3c01d1a65f5..8937ed467a1 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -12,13 +12,14 @@ use rustc_infer::traits::{
 use rustc_middle::bug;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_next_trait_solver::solve::{GenerateProofTree, SolverDelegateEvalExt as _};
 use rustc_span::symbol::sym;
 
 use crate::traits::{FulfillmentError, FulfillmentErrorCode, ScrubbedTraitError};
 
-use super::eval_ctxt::GenerateProofTree;
+use super::infcx::SolverDelegate;
 use super::inspect::{self, ProofTreeInferCtxtExt, ProofTreeVisitor};
-use super::{Certainty, InferCtxtEvalExt};
+use super::Certainty;
 
 /// A trait engine using the new trait solver.
 ///
@@ -83,7 +84,9 @@ impl<'tcx> ObligationStorage<'tcx> {
             // change.
             self.overflowed.extend(self.pending.extract_if(|o| {
                 let goal = o.clone().into();
-                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::No).0;
+                let result = <&SolverDelegate<'tcx>>::from(infcx)
+                    .evaluate_root_goal(goal, GenerateProofTree::No)
+                    .0;
                 match result {
                     Ok((has_changed, _)) => has_changed,
                     _ => false,
@@ -165,7 +168,9 @@ where
             let mut has_changed = false;
             for obligation in self.obligations.unstalled_for_select() {
                 let goal = obligation.clone().into();
-                let result = infcx.evaluate_root_goal(goal, GenerateProofTree::No).0;
+                let result = <&SolverDelegate<'tcx>>::from(infcx)
+                    .evaluate_root_goal(goal, GenerateProofTree::No)
+                    .0;
                 self.inspect_evaluated_obligation(infcx, &obligation, &result);
                 let (changed, certainty) = match result {
                     Ok(result) => result,
@@ -288,7 +293,10 @@ fn fulfillment_error_for_stalled<'tcx>(
     root_obligation: PredicateObligation<'tcx>,
 ) -> FulfillmentError<'tcx> {
     let (code, refine_obligation) = infcx.probe(|_| {
-        match infcx.evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No).0 {
+        match <&SolverDelegate<'tcx>>::from(infcx)
+            .evaluate_root_goal(root_obligation.clone().into(), GenerateProofTree::No)
+            .0
+        {
             Ok((_, Certainty::Maybe(MaybeCause::Ambiguity))) => {
                 (FulfillmentErrorCode::Ambiguity { overflow: None }, true)
             }
diff --git a/compiler/rustc_trait_selection/src/solve/infcx.rs b/compiler/rustc_trait_selection/src/solve/infcx.rs
new file mode 100644
index 00000000000..e574166cbfc
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/infcx.rs
@@ -0,0 +1,435 @@
+use std::ops::Deref;
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::{DefId, LocalDefId};
+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::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)]
+pub struct SolverDelegate<'tcx>(InferCtxt<'tcx>);
+
+impl<'a, 'tcx> From<&'a InferCtxt<'tcx>> for &'a SolverDelegate<'tcx> {
+    fn from(infcx: &'a InferCtxt<'tcx>) -> Self {
+        // SAFETY: `repr(transparent)`
+        unsafe { std::mem::transmute(infcx) }
+    }
+}
+
+impl<'tcx> Deref for SolverDelegate<'tcx> {
+    type Target = InferCtxt<'tcx>;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<'tcx> rustc_next_trait_solver::infcx::SolverDelegate for SolverDelegate<'tcx> {
+    type Interner = TyCtxt<'tcx>;
+
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.0.tcx
+    }
+
+    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,
+        canonical: &Canonical<'tcx, V>,
+    ) -> (Self, V, CanonicalVarValues<'tcx>)
+    where
+        V: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        let (infcx, value, vars) = interner
+            .infer_ctxt()
+            .with_next_trait_solver(true)
+            .intercrate(match solver_mode {
+                SolverMode::Normal => false,
+                SolverMode::Coherence => true,
+            })
+            .build_with_canonical(DUMMY_SP, canonical);
+        (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>,
+        span: Span,
+    ) -> ty::GenericArg<'tcx> {
+        match arg.unpack() {
+            ty::GenericArgKind::Lifetime(_) => {
+                self.next_region_var(RegionVariableOrigin::MiscVariable(span)).into()
+            }
+            ty::GenericArgKind::Type(_) => self.next_ty_var(span).into(),
+            ty::GenericArgKind::Const(_) => self.next_const_var(span).into(),
+        }
+    }
+
+    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>,
+        unevaluated: ty::UnevaluatedConst<'tcx>,
+    ) -> Option<ty::Const<'tcx>> {
+        use rustc_middle::mir::interpret::ErrorHandled;
+        match self.const_eval_resolve(param_env, unevaluated, DUMMY_SP) {
+            Ok(Some(val)) => Some(ty::Const::new_value(
+                self.tcx,
+                val,
+                self.tcx.type_of(unevaluated.def).instantiate(self.tcx, unevaluated.args),
+            )),
+            Ok(None) | Err(ErrorHandled::TooGeneric(_)) => None,
+            Err(ErrorHandled::Reported(e, _)) => Some(ty::Const::new_error(self.tcx, e.into())),
+        }
+    }
+
+    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>,
+        arg: ty::GenericArg<'tcx>,
+    ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> {
+        crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg).map(|obligations| {
+            obligations.into_iter().map(|obligation| obligation.into()).collect()
+        })
+    }
+
+    fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> {
+        self.0.clone_opaque_types_for_query_response()
+    }
+
+    fn make_deduplicated_outlives_constraints(
+        &self,
+    ) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
+        // Cannot use `take_registered_region_obligations` as we may compute the response
+        // inside of a `probe` whenever we have multiple choices inside of the solver.
+        let region_obligations = self.0.inner.borrow().region_obligations().to_owned();
+        let region_constraints = self.0.with_region_constraints(|region_constraints| {
+            make_query_region_constraints(
+                self.tcx,
+                region_obligations
+                    .iter()
+                    .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
+                region_constraints,
+            )
+        });
+
+        assert_eq!(region_constraints.member_constraints, vec![]);
+
+        let mut seen = FxHashSet::default();
+        region_constraints
+            .outlives
+            .into_iter()
+            .filter(|&(outlives, _)| seen.insert(outlives))
+            .map(|(outlives, _)| outlives)
+            .collect()
+    }
+
+    fn instantiate_canonical<V>(
+        &self,
+        canonical: Canonical<'tcx, V>,
+        values: CanonicalVarValues<'tcx>,
+    ) -> V
+    where
+        V: TypeFoldable<TyCtxt<'tcx>>,
+    {
+        canonical.instantiate(self.tcx, &values)
+    }
+
+    fn instantiate_canonical_var_with_infer(
+        &self,
+        cv_info: CanonicalVarInfo<'tcx>,
+        universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex,
+    ) -> ty::GenericArg<'tcx> {
+        self.0.instantiate_canonical_var(DUMMY_SP, cv_info, universe_map)
+    }
+
+    fn insert_hidden_type(
+        &self,
+        opaque_type_key: ty::OpaqueTypeKey<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        hidden_ty: Ty<'tcx>,
+        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) -> Result<(), NoSolution> {
+        self.0
+            .insert_hidden_type(opaque_type_key, DUMMY_SP, param_env, hidden_ty, goals)
+            .map_err(|_| NoSolution)
+    }
+
+    fn add_item_bounds_for_hidden_type(
+        &self,
+        def_id: DefId,
+        args: ty::GenericArgsRef<'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        hidden_ty: Ty<'tcx>,
+        goals: &mut Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
+    ) {
+        self.0.add_item_bounds_for_hidden_type(def_id, args, param_env, hidden_ty, goals);
+    }
+
+    fn inject_new_hidden_type_unchecked(&self, key: ty::OpaqueTypeKey<'tcx>, hidden_ty: Ty<'tcx>) {
+        self.0.inject_new_hidden_type_unchecked(
+            key,
+            ty::OpaqueHiddenType { ty: hidden_ty, span: DUMMY_SP },
+        )
+    }
+
+    fn reset_opaque_types(&self) {
+        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>,
+        goal_trait_ref: ty::TraitRef<'tcx>,
+        trait_assoc_def_id: DefId,
+        impl_def_id: DefId,
+    ) -> Result<Option<DefId>, NoSolution> {
+        let node_item = specialization_graph::assoc_def(self.tcx, impl_def_id, trait_assoc_def_id)
+            .map_err(|ErrorGuaranteed { .. }| NoSolution)?;
+
+        let eligible = if node_item.is_final() {
+            // Non-specializable items are always projectable.
+            true
+        } else {
+            // Only reveal a specializable default if we're past type-checking
+            // and the obligation is monomorphic, otherwise passes such as
+            // transmute checking and polymorphic MIR optimizations could
+            // get a result which isn't correct for all monomorphizations.
+            if param_env.reveal() == Reveal::All {
+                let poly_trait_ref = self.resolve_vars_if_possible(goal_trait_ref);
+                !poly_trait_ref.still_further_specializable()
+            } else {
+                trace!(?node_item.item.def_id, "not eligible due to default");
+                false
+            }
+        };
+
+        // FIXME: Check for defaultness here may cause diagnostics problems.
+        if eligible { Ok(Some(node_item.item.def_id)) } else { Ok(None) }
+    }
+
+    fn is_transmutable(
+        &self,
+        param_env: ty::ParamEnv<'tcx>,
+        dst: Ty<'tcx>,
+        src: Ty<'tcx>,
+        assume: ty::Const<'tcx>,
+    ) -> Result<Certainty, NoSolution> {
+        // Erase regions because we compute layouts in `rustc_transmute`,
+        // which will ICE for region vars.
+        let (dst, src) = self.tcx.erase_regions((dst, src));
+
+        let Some(assume) = rustc_transmute::Assume::from_const(self.tcx, param_env, assume) else {
+            return Err(NoSolution);
+        };
+
+        // FIXME(transmutability): This really should be returning nested goals for `Answer::If*`
+        match rustc_transmute::TransmuteTypeEnv::new(&self.0).is_transmutable(
+            ObligationCause::dummy(),
+            rustc_transmute::Types { src, dst },
+            assume,
+        ) {
+            rustc_transmute::Answer::Yes => Ok(Certainty::Yes),
+            rustc_transmute::Answer::No(_) | rustc_transmute::Answer::If(_) => Err(NoSolution),
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/inspect.rs b/compiler/rustc_trait_selection/src/solve/inspect.rs
new file mode 100644
index 00000000000..f100a8c2ff0
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/inspect.rs
@@ -0,0 +1,4 @@
+pub use rustc_next_trait_solver::solve::inspect::*;
+
+mod analyse;
+pub use analyse::*;
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
index 464c188b6e3..cb621487125 100644
--- a/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
+++ b/compiler/rustc_trait_selection/src/solve/inspect/analyse.rs
@@ -13,18 +13,16 @@ use rustc_ast_ir::try_visit;
 use rustc_ast_ir::visit::VisitorResult;
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, InferOk};
 use rustc_macros::extension;
-use rustc_middle::traits::query::NoSolution;
-use rustc_middle::traits::solve::{inspect, QueryResult};
-use rustc_middle::traits::solve::{Certainty, Goal, MaybeCause};
+use rustc_middle::traits::solve::{Certainty, Goal, GoalSource, NoSolution, QueryResult};
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{TyCtxt, TypeFoldable};
 use rustc_middle::{bug, ty};
 use rustc_next_trait_solver::resolve::EagerResolver;
+use rustc_next_trait_solver::solve::inspect::{self, instantiate_canonical_state};
+use rustc_next_trait_solver::solve::{GenerateProofTree, MaybeCause, SolverDelegateEvalExt as _};
 use rustc_span::{Span, DUMMY_SP};
 
-use crate::solve::eval_ctxt::canonical;
-use crate::solve::{EvalCtxt, GoalEvaluationKind, GoalSource};
-use crate::solve::{GenerateProofTree, InferCtxtEvalExt};
+use crate::solve::infcx::SolverDelegate;
 use crate::traits::ObligationCtxt;
 
 pub struct InspectConfig {
@@ -32,7 +30,7 @@ pub struct InspectConfig {
 }
 
 pub struct InspectGoal<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
+    infcx: &'a SolverDelegate<'tcx>,
     depth: usize,
     orig_values: Vec<ty::GenericArg<'tcx>>,
     goal: Goal<'tcx, ty::Predicate<'tcx>>,
@@ -162,16 +160,10 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             match **step {
                 inspect::ProbeStep::AddGoal(source, goal) => instantiated_goals.push((
                     source,
-                    canonical::instantiate_canonical_state(
-                        infcx,
-                        span,
-                        param_env,
-                        &mut orig_values,
-                        goal,
-                    ),
+                    instantiate_canonical_state(infcx, span, param_env, &mut orig_values, goal),
                 )),
                 inspect::ProbeStep::RecordImplArgs { impl_args } => {
-                    opt_impl_args = Some(canonical::instantiate_canonical_state(
+                    opt_impl_args = Some(instantiate_canonical_state(
                         infcx,
                         span,
                         param_env,
@@ -184,13 +176,8 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
             }
         }
 
-        let () = canonical::instantiate_canonical_state(
-            infcx,
-            span,
-            param_env,
-            &mut orig_values,
-            self.final_state,
-        );
+        let () =
+            instantiate_canonical_state(infcx, span, param_env, &mut orig_values, self.final_state);
 
         if let Some(term_hack) = self.goal.normalizes_to_term_hack {
             // FIXME: We ignore the expected term of `NormalizesTo` goals
@@ -219,16 +206,7 @@ impl<'a, 'tcx> InspectCandidate<'a, 'tcx> {
                     // instantiating the candidate it is already constrained to the result of another
                     // candidate.
                     let proof_tree = infcx
-                        .probe(|_| {
-                            EvalCtxt::enter_root(infcx, GenerateProofTree::Yes, |ecx| {
-                                ecx.evaluate_goal_raw(
-                                    GoalEvaluationKind::Root,
-                                    GoalSource::Misc,
-                                    goal,
-                                )
-                            })
-                        })
-                        .1;
+                        .probe(|_| infcx.evaluate_root_goal_raw(goal, GenerateProofTree::Yes).1);
                     InspectGoal::new(
                         infcx,
                         self.goal.depth + 1,
@@ -388,6 +366,8 @@ impl<'a, 'tcx> InspectGoal<'a, 'tcx> {
         normalizes_to_term_hack: Option<NormalizesToTermHack<'tcx>>,
         source: GoalSource,
     ) -> Self {
+        let infcx = <&SolverDelegate<'tcx>>::from(infcx);
+
         let inspect::GoalEvaluation { uncanonicalized_goal, orig_values, evaluation } = root;
         let result = evaluation.result.and_then(|ok| {
             if let Some(term_hack) = normalizes_to_term_hack {
@@ -449,7 +429,8 @@ impl<'tcx> InferCtxt<'tcx> {
         depth: usize,
         visitor: &mut V,
     ) -> V::Result {
-        let (_, proof_tree) = self.evaluate_root_goal(goal, GenerateProofTree::Yes);
+        let (_, proof_tree) =
+            <&SolverDelegate<'tcx>>::from(self).evaluate_root_goal(goal, GenerateProofTree::Yes);
         let proof_tree = proof_tree.unwrap();
         visitor.visit_goal(&InspectGoal::new(self, depth, proof_tree, None, GoalSource::Misc))
     }
diff --git a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs b/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
deleted file mode 100644
index 60d52305a6b..00000000000
--- a/compiler/rustc_trait_selection/src/solve/inspect/mod.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-pub use rustc_middle::traits::solve::inspect::*;
-
-mod build;
-pub(in crate::solve) use build::*;
-
-mod analyse;
-pub use analyse::*;
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index f632f1ad4f2..038f11c60b8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -4705,14 +4705,11 @@ fn hint_missing_borrow<'tcx>(
     }
 
     if !to_borrow.is_empty() {
-        err.subdiagnostic(infcx.dcx(), errors::AdjustSignatureBorrow::Borrow { to_borrow });
+        err.subdiagnostic(errors::AdjustSignatureBorrow::Borrow { to_borrow });
     }
 
     if !remove_borrow.is_empty() {
-        err.subdiagnostic(
-            infcx.dcx(),
-            errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow },
-        );
+        err.subdiagnostic(errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow });
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index b4019585771..fe047f9966f 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -19,7 +19,7 @@ use super::{
 };
 
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
-use crate::solve::InferCtxtSelectExt;
+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;
diff --git a/compiler/rustc_type_ir/src/infcx.rs b/compiler/rustc_type_ir/src/infcx.rs
deleted file mode 100644
index 4a5f34e3542..00000000000
--- a/compiler/rustc_type_ir/src/infcx.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use crate::fold::TypeFoldable;
-use crate::relate::Relate;
-use crate::solve::{Goal, NoSolution};
-use crate::{self as ty, Interner};
-
-pub trait InferCtxtLike: Sized {
-    type Interner: Interner;
-
-    fn interner(&self) -> Self::Interner;
-
-    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 resolve_vars_if_possible<T>(&self, value: T) -> T
-    where
-        T: TypeFoldable<Self::Interner>;
-
-    fn probe<T>(&self, probe: impl FnOnce() -> T) -> T;
-}
diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs
index 6b84592978a..64d3400976a 100644
--- a/compiler/rustc_type_ir/src/inherent.rs
+++ b/compiler/rustc_type_ir/src/inherent.rs
@@ -8,9 +8,11 @@ use std::hash::Hash;
 use std::ops::Deref;
 
 use rustc_ast_ir::Mutability;
+use rustc_data_structures::fx::FxHashSet;
 
 use crate::fold::{TypeFoldable, TypeSuperFoldable};
 use crate::relate::Relate;
+use crate::solve::{CacheData, CanonicalInput, QueryResult, Reveal};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty, CollectAndApply, Interner, UpcastFrom};
 
@@ -27,10 +29,14 @@ pub trait Ty<I: Interner<Ty = Self>>:
     + Relate<I>
     + Flags
 {
+    fn new_unit(interner: I) -> Self;
+
     fn new_bool(interner: I) -> Self;
 
     fn new_u8(interner: I) -> Self;
 
+    fn new_usize(interner: I) -> Self;
+
     fn new_infer(interner: I, var: ty::InferTy) -> Self;
 
     fn new_var(interner: I, var: ty::TyVid) -> Self;
@@ -107,6 +113,10 @@ pub trait Ty<I: Interner<Ty = Self>>:
         matches!(self.kind(), ty::Infer(ty::TyVar(_)))
     }
 
+    fn is_fn_ptr(self) -> bool {
+        matches!(self.kind(), ty::FnPtr(_))
+    }
+
     fn fn_sig(self, interner: I) -> ty::Binder<I, ty::FnSig<I>> {
         match self.kind() {
             ty::FnPtr(sig) => sig,
@@ -126,6 +136,49 @@ pub trait Ty<I: Interner<Ty = Self>>:
             _ => panic!("Ty::fn_sig() called on non-fn type: {:?}", self),
         }
     }
+
+    fn discriminant_ty(self, interner: I) -> I::Ty;
+
+    fn async_destructor_ty(self, interner: I) -> I::Ty;
+
+    /// Returns `true` when the outermost type cannot be further normalized,
+    /// resolved, or instantiated. This includes all primitive types, but also
+    /// things like ADTs and trait objects, sice even if their arguments or
+    /// nested types may be further simplified, the outermost [`ty::TyKind`] or
+    /// type constructor remains the same.
+    fn is_known_rigid(self) -> bool {
+        match self.kind() {
+            ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Adt(_, _)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(_, _)
+            | ty::Pat(_, _)
+            | ty::Slice(_)
+            | ty::RawPtr(_, _)
+            | ty::Ref(_, _, _)
+            | ty::FnDef(_, _)
+            | ty::FnPtr(_)
+            | ty::Dynamic(_, _, _)
+            | ty::Closure(_, _)
+            | ty::CoroutineClosure(_, _)
+            | ty::Coroutine(_, _)
+            | ty::CoroutineWitness(..)
+            | ty::Never
+            | ty::Tuple(_) => true,
+
+            ty::Error(_)
+            | ty::Infer(_)
+            | ty::Alias(_, _)
+            | ty::Param(_)
+            | ty::Bound(_, _)
+            | ty::Placeholder(_) => false,
+        }
+    }
 }
 
 pub trait Tys<I: Interner<Tys = Self>>:
@@ -200,6 +253,12 @@ pub trait Const<I: Interner<Const = Self>>:
 
     fn new_expr(interner: I, expr: I::ExprConst) -> Self;
 
+    fn new_error(interner: I, guar: I::ErrorGuaranteed) -> Self;
+
+    fn new_error_with_message(interner: I, msg: impl ToString) -> Self {
+        Self::new_error(interner, interner.delay_bug(msg))
+    }
+
     fn is_ct_var(self) -> bool {
         matches!(self.kind(), ty::ConstKind::Infer(ty::InferConst::Var(_)))
     }
@@ -221,6 +280,37 @@ pub trait GenericArg<I: Interner<GenericArg = Self>>:
     + From<I::Region>
     + From<I::Const>
 {
+    fn as_type(&self) -> Option<I::Ty> {
+        if let ty::GenericArgKind::Type(ty) = self.kind() { Some(ty) } else { None }
+    }
+
+    fn expect_ty(&self) -> I::Ty {
+        self.as_type().expect("expected a type")
+    }
+
+    fn as_const(&self) -> Option<I::Const> {
+        if let ty::GenericArgKind::Const(c) = self.kind() { Some(c) } else { None }
+    }
+
+    fn expect_const(&self) -> I::Const {
+        self.as_const().expect("expected a const")
+    }
+
+    fn as_region(&self) -> Option<I::Region> {
+        if let ty::GenericArgKind::Lifetime(c) = self.kind() { Some(c) } else { None }
+    }
+
+    fn expect_region(&self) -> I::Region {
+        self.as_region().expect("expected a const")
+    }
+
+    fn is_non_region_infer(self) -> bool {
+        match self.kind() {
+            ty::GenericArgKind::Lifetime(_) => false,
+            ty::GenericArgKind::Type(ty) => ty.is_ty_var(),
+            ty::GenericArgKind::Const(ct) => ct.is_ct_var(),
+        }
+    }
 }
 
 pub trait Term<I: Interner<Term = Self>>:
@@ -230,7 +320,7 @@ pub trait Term<I: Interner<Term = Self>>:
         if let ty::TermKind::Ty(ty) = self.kind() { Some(ty) } else { None }
     }
 
-    fn expect_type(&self) -> I::Ty {
+    fn expect_ty(&self) -> I::Ty {
         self.as_type().expect("expected a type, but found a const")
     }
 
@@ -248,6 +338,19 @@ pub trait Term<I: Interner<Term = Self>>:
             ty::TermKind::Const(ct) => ct.is_ct_var(),
         }
     }
+
+    fn to_alias_term(self) -> Option<ty::AliasTerm<I>> {
+        match self.kind() {
+            ty::TermKind::Ty(ty) => match ty.kind() {
+                ty::Alias(_kind, alias_ty) => Some(alias_ty.into()),
+                _ => None,
+            },
+            ty::TermKind::Const(ct) => match ct.kind() {
+                ty::ConstKind::Unevaluated(uv) => Some(uv.into()),
+                _ => None,
+            },
+        }
+    }
 }
 
 pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
@@ -260,8 +363,19 @@ pub trait GenericArgs<I: Interner<GenericArgs = Self>>:
     + Default
     + Relate<I>
 {
+    fn rebase_onto(
+        self,
+        interner: I,
+        source_def_id: I::DefId,
+        target: I::GenericArgs,
+    ) -> I::GenericArgs;
+
     fn type_at(self, i: usize) -> I::Ty;
 
+    fn region_at(self, i: usize) -> I::Region;
+
+    fn const_at(self, i: usize) -> I::Const;
+
     fn identity_for_item(interner: I, def_id: I::DefId) -> I::GenericArgs;
 
     fn extend_with_error(
@@ -301,6 +415,9 @@ pub trait Predicate<I: Interner<Predicate = Self>>:
     + UpcastFrom<I, ty::NormalizesTo<I>>
     + UpcastFrom<I, ty::TraitRef<I>>
     + UpcastFrom<I, ty::Binder<I, ty::TraitRef<I>>>
+    + UpcastFrom<I, ty::TraitPredicate<I>>
+    + UpcastFrom<I, ty::OutlivesPredicate<I, I::Ty>>
+    + UpcastFrom<I, ty::OutlivesPredicate<I, I::Region>>
     + IntoKind<Kind = ty::Binder<I, ty::PredicateKind<I>>>
 {
     fn is_coinductive(self, interner: I) -> bool;
@@ -316,9 +433,34 @@ pub trait Clause<I: Interner<Clause = Self>>:
     + Eq
     + TypeFoldable<I>
     // FIXME: Remove these, uplift the `Upcast` impls.
+    + 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>>>
 {
+    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
+                }
+            })
+            .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
+                }
+            })
+            .transpose()
+    }
 }
 
 /// Common capabilities of placeholder kinds
@@ -350,16 +492,81 @@ pub trait ParamLike {
 pub trait AdtDef<I: Interner>: Copy + Debug + Hash + Eq {
     fn def_id(self) -> I::DefId;
 
+    fn is_struct(self) -> bool;
+
+    /// Returns the type of the struct tail.
+    ///
+    /// Expects the `AdtDef` to be a struct. If it is not, then this will panic.
+    fn struct_tail_ty(self, interner: I) -> Option<ty::EarlyBinder<I, I::Ty>>;
+
     fn is_phantom_data(self) -> bool;
 
     // FIXME: perhaps use `all_fields` and expose `FieldDef`.
-    fn all_field_tys(self, interner: I) -> ty::EarlyBinder<I, impl Iterator<Item = I::Ty>>;
+    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>>;
 }
 
+pub trait ParamEnv<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn reveal(self) -> Reveal;
+
+    fn caller_bounds(self) -> impl IntoIterator<Item = I::Clause>;
+}
+
 pub trait Features<I: Interner>: Copy {
     fn generic_const_exprs(self) -> bool;
 
     fn coroutine_clone(self) -> bool;
+
+    fn associated_const_equality(self) -> bool;
+}
+
+pub trait EvaluationCache<I: Interner> {
+    /// Insert a final result into the global cache.
+    fn insert(
+        &self,
+        tcx: I,
+        key: CanonicalInput<I>,
+        proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
+        additional_depth: usize,
+        encountered_overflow: bool,
+        cycle_participants: FxHashSet<CanonicalInput<I>>,
+        dep_node: I::DepNodeIndex,
+        result: QueryResult<I>,
+    );
+
+    /// Try to fetch a cached result, checking the recursion limit
+    /// and handling root goals of coinductive cycles.
+    ///
+    /// If this returns `Some` the cache result can be used.
+    fn get(
+        &self,
+        tcx: I,
+        key: CanonicalInput<I>,
+        stack_entries: impl IntoIterator<Item = CanonicalInput<I>>,
+        available_depth: usize,
+    ) -> Option<CacheData<I>>;
+}
+
+pub trait DefId<I: Interner>: Copy + Debug + Hash + Eq + TypeFoldable<I> {
+    fn as_local(self) -> Option<I::LocalDefId>;
+}
+
+pub trait BoundExistentialPredicates<I: Interner>:
+    Copy
+    + Debug
+    + Hash
+    + Eq
+    + Relate<I>
+    + IntoIterator<Item = ty::Binder<I, ty::ExistentialPredicate<I>>>
+{
+    fn principal_def_id(self) -> Option<I::DefId>;
+
+    fn principal(self) -> Option<ty::Binder<I, ty::ExistentialTraitRef<I>>>;
+
+    fn auto_traits(self) -> impl IntoIterator<Item = I::DefId>;
+
+    fn projection_bounds(
+        self,
+    ) -> impl IntoIterator<Item = ty::Binder<I, ty::ExistentialProjection<I>>>;
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 11c1f73fef3..59ca95c09cd 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -1,4 +1,5 @@
 use rustc_ast_ir::Movability;
+use rustc_index::bit_set::BitSet;
 use smallvec::SmallVec;
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -10,6 +11,7 @@ use crate::ir_print::IrPrint;
 use crate::lang_items::TraitSolverLangItem;
 use crate::relate::Relate;
 use crate::solve::inspect::CanonicalGoalEvaluationStep;
+use crate::solve::{ExternalConstraintsData, PredefinedOpaquesData, SolverMode};
 use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{self as ty};
 
@@ -28,9 +30,8 @@ pub trait Interner:
     + IrPrint<ty::CoercePredicate<Self>>
     + IrPrint<ty::FnSig<Self>>
 {
-    type DefId: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type DefId: DefId<Self>;
     type LocalDefId: Copy + Debug + Hash + Eq + Into<Self::DefId> + TypeFoldable<Self>;
-    type AdtDef: AdtDef<Self>;
 
     type GenericArgs: GenericArgs<Self>;
     type GenericArgsSlice: Copy + Debug + Hash + Eq + Deref<Target = [Self::GenericArg]>;
@@ -45,16 +46,53 @@ pub trait Interner:
         + Default;
     type BoundVarKind: Copy + Debug + Hash + Eq;
 
-    type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = ty::CanonicalVarInfo<Self>>;
-    type PredefinedOpaques: Copy + Debug + Hash + Eq;
-    type DefiningOpaqueTypes: Copy + Debug + Hash + Default + Eq + TypeVisitable<Self>;
-    type ExternalConstraints: Copy + Debug + Hash + Eq;
+    type PredefinedOpaques: Copy
+        + Debug
+        + Hash
+        + Eq
+        + TypeFoldable<Self>
+        + Deref<Target = PredefinedOpaquesData<Self>>;
+    fn mk_predefined_opaques_in_body(
+        self,
+        data: PredefinedOpaquesData<Self>,
+    ) -> Self::PredefinedOpaques;
+
+    type DefiningOpaqueTypes: Copy
+        + Debug
+        + Hash
+        + Default
+        + Eq
+        + TypeVisitable<Self>
+        + Deref<Target: Deref<Target = [Self::LocalDefId]>>;
     type CanonicalGoalEvaluationStepRef: Copy
         + Debug
         + Hash
         + Eq
         + Deref<Target = CanonicalGoalEvaluationStep<Self>>;
 
+    type CanonicalVars: Copy
+        + Debug
+        + Hash
+        + Eq
+        + IntoIterator<Item = ty::CanonicalVarInfo<Self>>
+        + Deref<Target: Deref<Target = [ty::CanonicalVarInfo<Self>]>>
+        + Default;
+    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
+
+    type ExternalConstraints: Copy
+        + Debug
+        + Hash
+        + Eq
+        + TypeFoldable<Self>
+        + Deref<Target = ExternalConstraintsData<Self>>;
+    fn mk_external_constraints(
+        self,
+        data: ExternalConstraintsData<Self>,
+    ) -> Self::ExternalConstraints;
+
+    type DepNodeIndex;
+    fn with_cached_task<T>(self, task: impl FnOnce() -> T) -> (T, Self::DepNodeIndex);
+
     // Kinds of tys
     type Ty: Ty<Self>;
     type Tys: Tys<Self>;
@@ -65,12 +103,7 @@ pub trait Interner:
 
     // Things stored inside of tys
     type ErrorGuaranteed: Copy + Debug + Hash + Eq;
-    type BoundExistentialPredicates: Copy
-        + Debug
-        + Hash
-        + Eq
-        + Relate<Self>
-        + IntoIterator<Item = ty::Binder<Self, ty::ExistentialPredicate<Self>>>;
+    type BoundExistentialPredicates: BoundExistentialPredicates<Self>;
     type AllocId: Copy + Debug + Hash + Eq;
     type Pat: Copy + Debug + Hash + Eq + Debug + Relate<Self>;
     type Safety: Safety<Self>;
@@ -92,14 +125,15 @@ pub trait Interner:
     type PlaceholderRegion: PlaceholderLike;
 
     // Predicates
-    type ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable<Self>;
+    type ParamEnv: ParamEnv<Self>;
     type Predicate: Predicate<Self>;
     type Clause: Clause<Self>;
     type Clauses: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
 
-    fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
+    type EvaluationCache: EvaluationCache<Self>;
+    fn evaluation_cache(self, mode: SolverMode) -> Self::EvaluationCache;
 
-    fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
+    fn expand_abstract_consts<T: TypeFoldable<Self>>(self, t: T) -> T;
 
     type GenericsOf: GenericsOf<Self>;
     fn generics_of(self, def_id: Self::DefId) -> Self::GenericsOf;
@@ -111,9 +145,11 @@ pub trait Interner:
         + IntoIterator<Item: Deref<Target = ty::Variance>>;
     fn variances_of(self, def_id: Self::DefId) -> Self::VariancesOf;
 
-    // FIXME: Remove after uplifting `EarlyBinder`
     fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder<Self, Self::Ty>;
 
+    type AdtDef: AdtDef<Self>;
+    fn adt_def(self, adt_def_id: Self::DefId) -> Self::AdtDef;
+
     fn alias_ty_kind(self, alias: ty::AliasTy<Self>) -> ty::AliasTyKind;
 
     fn alias_term_kind(self, alias: ty::AliasTerm<Self>) -> ty::AliasTermKind;
@@ -131,6 +167,8 @@ pub trait Interner:
         I: Iterator<Item = T>,
         T: CollectAndApply<Self::GenericArg, Self::GenericArgs>;
 
+    fn check_args_compatible(self, def_id: Self::DefId, args: Self::GenericArgs) -> bool;
+
     fn check_and_mk_args(
         self,
         def_id: Self::DefId,
@@ -175,6 +213,17 @@ pub trait Interner:
         def_id: Self::DefId,
     ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
 
+    fn predicates_of(
+        self,
+        def_id: Self::DefId,
+    ) -> ty::EarlyBinder<Self, impl IntoIterator<Item = Self::Clause>>;
+
+    fn own_predicates_of(
+        self,
+        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(
         self,
         def_id: Self::DefId,
@@ -184,7 +233,64 @@ pub trait Interner:
 
     fn require_lang_item(self, lang_item: TraitSolverLangItem) -> Self::DefId;
 
+    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>;
+
+    // FIXME: move `fast_reject` into `rustc_type_ir`.
+    fn args_may_unify_deep(
+        self,
+        obligation_args: Self::GenericArgs,
+        impl_args: Self::GenericArgs,
+    ) -> bool;
+
+    fn for_each_relevant_impl(
+        self,
+        trait_def_id: Self::DefId,
+        self_ty: Self::Ty,
+        f: impl FnMut(Self::DefId),
+    );
+
+    fn has_item_definition(self, def_id: Self::DefId) -> bool;
+
+    fn impl_is_default(self, impl_def_id: Self::DefId) -> bool;
+
+    fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>;
+
+    fn impl_polarity(self, impl_def_id: Self::DefId) -> ty::ImplPolarity;
+
+    fn trait_is_auto(self, trait_def_id: Self::DefId) -> bool;
+
+    fn trait_is_alias(self, trait_def_id: Self::DefId) -> bool;
+
+    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 supertrait_def_ids(self, trait_def_id: Self::DefId)
+    -> impl IntoIterator<Item = Self::DefId>;
+
+    fn delay_bug(self, msg: impl ToString) -> Self::ErrorGuaranteed;
+
+    fn is_general_coroutine(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_async(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_gen(self, coroutine_def_id: Self::DefId) -> bool;
+    fn coroutine_is_async_gen(self, coroutine_def_id: Self::DefId) -> bool;
+
+    fn layout_is_pointer_like(self, param_env: Self::ParamEnv, ty: Self::Ty) -> bool;
+
+    type UnsizingParams: Deref<Target = BitSet<u32>>;
+    fn unsizing_params_for_adt(self, adt_def_id: Self::DefId) -> Self::UnsizingParams;
+
+    fn find_const_ty_from_env(
+        self,
+        param_env: Self::ParamEnv,
+        placeholder: Self::PlaceholderConst,
+    ) -> Self::Ty;
 }
 
 /// 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 9a3b324fcd7..cf5ec1ab3fe 100644
--- a/compiler/rustc_type_ir/src/lang_items.rs
+++ b/compiler/rustc_type_ir/src/lang_items.rs
@@ -1,8 +1,36 @@
 /// Lang items used by the new trait solver. This can be mapped to whatever internal
 /// representation of `LangItem`s used in the underlying compiler implementation.
 pub enum TraitSolverLangItem {
-    Future,
-    FutureOutput,
+    // tidy-alphabetical-start
+    AsyncDestruct,
     AsyncFnKindHelper,
     AsyncFnKindUpvars,
+    AsyncFnOnceOutput,
+    AsyncIterator,
+    CallOnceFuture,
+    CallRefFuture,
+    Clone,
+    Copy,
+    Coroutine,
+    CoroutineReturn,
+    CoroutineYield,
+    Destruct,
+    DiscriminantKind,
+    DynMetadata,
+    FnPtrTrait,
+    FusedIterator,
+    Future,
+    FutureOutput,
+    Iterator,
+    Metadata,
+    Option,
+    PointeeTrait,
+    PointerLike,
+    Poll,
+    Sized,
+    TransmuteTrait,
+    Tuple,
+    Unpin,
+    Unsize,
+    // tidy-alphabetical-end
 }
diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index ac9b2808804..130ea231bf7 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -45,7 +45,6 @@ mod canonical;
 mod const_kind;
 mod flags;
 mod generic_arg;
-mod infcx;
 mod interner;
 mod opaque_ty;
 mod predicate;
@@ -62,7 +61,6 @@ pub use codec::*;
 pub use const_kind::*;
 pub use flags::*;
 pub use generic_arg::*;
-pub use infcx::InferCtxtLike;
 pub use interner::*;
 pub use opaque_ty::*;
 pub use predicate::*;
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index c0713dc50d2..bf39f920276 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -7,7 +7,7 @@ use rustc_type_ir_macros::{Lift_Generic, TypeFoldable_Generic, TypeVisitable_Gen
 
 use crate::inherent::*;
 use crate::lift::Lift;
-use crate::upcast::Upcast;
+use crate::upcast::{Upcast, UpcastFrom};
 use crate::visit::TypeVisitableExt as _;
 use crate::{self as ty, Interner};
 
@@ -166,6 +166,12 @@ impl<I: Interner> ty::Binder<I, TraitPredicate<I>> {
     }
 }
 
+impl<I: Interner> UpcastFrom<I, TraitRef<I>> for TraitPredicate<I> {
+    fn upcast_from(from: TraitRef<I>, _tcx: I) -> Self {
+        TraitPredicate { trait_ref: from, polarity: PredicatePolarity::Positive }
+    }
+}
+
 impl<I: Interner> fmt::Debug for TraitPredicate<I> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         // FIXME(effects) printing?
diff --git a/compiler/rustc_type_ir/src/solve.rs b/compiler/rustc_type_ir/src/solve.rs
index 99d2fa74494..7934f996f0b 100644
--- a/compiler/rustc_type_ir/src/solve.rs
+++ b/compiler/rustc_type_ir/src/solve.rs
@@ -57,6 +57,19 @@ pub enum Reveal {
     All,
 }
 
+#[derive(Debug, Clone, Copy)]
+pub enum SolverMode {
+    /// Ordinary trait solving, using everywhere except for coherence.
+    Normal,
+    /// Trait solving during coherence. There are a few notable differences
+    /// between coherence and ordinary trait solving.
+    ///
+    /// Most importantly, trait solving during coherence must not be incomplete,
+    /// i.e. return `Err(NoSolution)` for goals for which a solution exists.
+    /// This means that we must not make any guesses or arbitrary choices.
+    Coherence,
+}
+
 pub type CanonicalInput<I, T = <I as Interner>::Predicate> = Canonical<I, QueryInput<I, T>>;
 pub type CanonicalResponse<I> = Canonical<I, Response<I>>;
 /// The result of evaluating a canonical query.
@@ -143,6 +156,22 @@ pub struct QueryInput<I: Interner, P> {
     pub predefined_opaques_in_body: I::PredefinedOpaques,
 }
 
+/// Opaques that are defined in the inference context before a query is called.
+#[derive(derivative::Derivative)]
+#[derivative(
+    Clone(bound = ""),
+    Hash(bound = ""),
+    PartialEq(bound = ""),
+    Eq(bound = ""),
+    Debug(bound = ""),
+    Default(bound = "")
+)]
+#[derive(TypeVisitable_Generic, TypeFoldable_Generic)]
+#[cfg_attr(feature = "nightly", derive(TyDecodable, TyEncodable, HashStable_NoContext))]
+pub struct PredefinedOpaquesData<I: Interner> {
+    pub opaque_types: Vec<(ty::OpaqueTypeKey<I>, I::Ty)>,
+}
+
 /// Possible ways the given goal can be proven.
 #[derive(derivative::Derivative)]
 #[derivative(
@@ -356,3 +385,12 @@ impl MaybeCause {
         }
     }
 }
+
+#[derive(derivative::Derivative)]
+#[derivative(PartialEq(bound = ""), Eq(bound = ""), Debug(bound = ""))]
+pub struct CacheData<I: Interner> {
+    pub result: QueryResult<I>,
+    pub proof_tree: Option<I::CanonicalGoalEvaluationStepRef>,
+    pub additional_depth: usize,
+    pub encountered_overflow: bool,
+}
diff --git a/library/core/src/panic.rs b/library/core/src/panic.rs
index b5a0932221a..56ede02673c 100644
--- a/library/core/src/panic.rs
+++ b/library/core/src/panic.rs
@@ -12,6 +12,8 @@ use crate::any::Any;
 pub use self::location::Location;
 #[stable(feature = "panic_hooks", since = "1.10.0")]
 pub use self::panic_info::PanicInfo;
+#[unstable(feature = "panic_info_message", issue = "66745")]
+pub use self::panic_info::PanicMessage;
 #[stable(feature = "catch_unwind", since = "1.9.0")]
 pub use self::unwind_safe::{AssertUnwindSafe, RefUnwindSafe, UnwindSafe};
 
diff --git a/library/core/src/panic/panic_info.rs b/library/core/src/panic/panic_info.rs
index 78cf1d2e98e..91953fd656b 100644
--- a/library/core/src/panic/panic_info.rs
+++ b/library/core/src/panic/panic_info.rs
@@ -1,4 +1,4 @@
-use crate::fmt;
+use crate::fmt::{self, Display};
 use crate::panic::Location;
 
 /// A struct providing information about a panic.
@@ -18,6 +18,17 @@ pub struct PanicInfo<'a> {
     force_no_backtrace: bool,
 }
 
+/// A message that was given to the `panic!()` macro.
+///
+/// The [`Display`] implementation of this type will format the message with the arguments
+/// that were given to the `panic!()` macro.
+///
+/// See [`PanicInfo::message`].
+#[unstable(feature = "panic_info_message", issue = "66745")]
+pub struct PanicMessage<'a> {
+    message: fmt::Arguments<'a>,
+}
+
 impl<'a> PanicInfo<'a> {
     #[inline]
     pub(crate) fn new(
@@ -29,12 +40,26 @@ impl<'a> PanicInfo<'a> {
         PanicInfo { location, message, can_unwind, force_no_backtrace }
     }
 
-    /// The message that was given to the `panic!` macro,
-    /// ready to be formatted with e.g. [`fmt::write`].
+    /// The message that was given to the `panic!` macro.
+    ///
+    /// # Example
+    ///
+    /// The type returned by this method implements `Display`, so it can
+    /// be passed directly to [`write!()`] and similar macros.
+    ///
+    /// [`write!()`]: core::write
+    ///
+    /// ```ignore (no_std)
+    /// #[panic_handler]
+    /// fn panic_handler(panic_info: &PanicInfo<'_>) -> ! {
+    ///     write!(DEBUG_OUTPUT, "panicked: {}", panic_info.message());
+    ///     loop {}
+    /// }
+    /// ```
     #[must_use]
     #[unstable(feature = "panic_info_message", issue = "66745")]
-    pub fn message(&self) -> fmt::Arguments<'_> {
-        self.message
+    pub fn message(&self) -> PanicMessage<'_> {
+        PanicMessage { message: self.message }
     }
 
     /// Returns information about the location from which the panic originated,
@@ -116,7 +141,7 @@ impl<'a> PanicInfo<'a> {
 }
 
 #[stable(feature = "panic_hook_display", since = "1.26.0")]
-impl fmt::Display for PanicInfo<'_> {
+impl Display for PanicInfo<'_> {
     fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
         formatter.write_str("panicked at ")?;
         self.location.fmt(formatter)?;
@@ -125,3 +150,41 @@ impl fmt::Display for PanicInfo<'_> {
         Ok(())
     }
 }
+
+impl<'a> PanicMessage<'a> {
+    /// Get the formatted message, if it has no arguments to be formatted at runtime.
+    ///
+    /// This can be used to avoid allocations in some cases.
+    ///
+    /// # Guarantees
+    ///
+    /// For `panic!("just a literal")`, this function is guaranteed to
+    /// return `Some("just a literal")`.
+    ///
+    /// For most cases with placeholders, this function will return `None`.
+    ///
+    /// See [`fmt::Arguments::as_str`] for details.
+    #[unstable(feature = "panic_info_message", issue = "66745")]
+    #[rustc_const_unstable(feature = "const_arguments_as_str", issue = "103900")]
+    #[must_use]
+    #[inline]
+    pub const fn as_str(&self) -> Option<&'static str> {
+        self.message.as_str()
+    }
+}
+
+#[unstable(feature = "panic_info_message", issue = "66745")]
+impl Display for PanicMessage<'_> {
+    #[inline]
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter.write_fmt(self.message)
+    }
+}
+
+#[unstable(feature = "panic_info_message", issue = "66745")]
+impl fmt::Debug for PanicMessage<'_> {
+    #[inline]
+    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+        formatter.write_fmt(self.message)
+    }
+}
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index 2bb5ea28b18..ebd05415695 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -593,19 +593,18 @@ pub fn panicking() -> bool {
 #[panic_handler]
 pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
     struct FormatStringPayload<'a> {
-        inner: &'a fmt::Arguments<'a>,
+        inner: &'a core::panic::PanicMessage<'a>,
         string: Option<String>,
     }
 
     impl FormatStringPayload<'_> {
         fn fill(&mut self) -> &mut String {
-            use crate::fmt::Write;
-
             let inner = self.inner;
             // Lazily, the first time this gets called, run the actual string formatting.
             self.string.get_or_insert_with(|| {
                 let mut s = String::new();
-                let _err = s.write_fmt(*inner);
+                let mut fmt = fmt::Formatter::new(&mut s);
+                let _err = fmt::Display::fmt(&inner, &mut fmt);
                 s
             })
         }
@@ -627,7 +626,11 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
 
     impl fmt::Display for FormatStringPayload<'_> {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            if let Some(s) = &self.string { f.write_str(s) } else { f.write_fmt(*self.inner) }
+            if let Some(s) = &self.string {
+                f.write_str(s)
+            } else {
+                fmt::Display::fmt(&self.inner, f)
+            }
         }
     }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2dcbbf0d150..da41f974068 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -228,6 +228,8 @@ fn clean_generic_bound<'tcx>(
 
             GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier)
         }
+        // FIXME(precise_capturing): Implement rustdoc support
+        hir::GenericBound::Use(..) => return None,
     })
 }
 
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 78d4129f642..45bd1616e83 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -8,6 +8,7 @@ use std::path::PathBuf;
 use std::str::FromStr;
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::DiagCtxtHandle;
 use rustc_session::config::{
     self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType,
 };
@@ -383,9 +384,10 @@ impl Options {
         };
 
         let dcx = new_dcx(error_format, None, diagnostic_width, &unstable_opts);
+        let dcx = dcx.handle();
 
         // check for deprecated options
-        check_deprecated_options(matches, &dcx);
+        check_deprecated_options(matches, dcx);
 
         if matches.opt_strs("passes") == ["list"] {
             println!("Available passes for running rustdoc:");
@@ -458,7 +460,7 @@ impl Options {
             println!("rustdoc: [check-theme] Starting tests! (Ignoring all other arguments)");
             for theme_file in to_check.iter() {
                 print!(" - Checking \"{theme_file}\"...");
-                let (success, differences) = theme::test_theme_against(theme_file, &paths, &dcx);
+                let (success, differences) = theme::test_theme_against(theme_file, &paths, dcx);
                 if !differences.is_empty() || !success {
                     println!(" FAILED");
                     errors += 1;
@@ -603,7 +605,7 @@ impl Options {
                         .with_help("arguments to --theme must have a .css extension")
                         .emit();
                 }
-                let (success, ret) = theme::test_theme_against(&theme_file, &paths, &dcx);
+                let (success, ret) = theme::test_theme_against(&theme_file, &paths, dcx);
                 if !success {
                     dcx.fatal(format!("error loading theme file: \"{theme_s}\""));
                 } else if !ret.is_empty() {
@@ -630,7 +632,7 @@ impl Options {
             &matches.opt_strs("markdown-before-content"),
             &matches.opt_strs("markdown-after-content"),
             nightly_options::match_is_nightly_build(matches),
-            &dcx,
+            dcx,
             &mut id_map,
             edition,
             &None,
@@ -741,9 +743,9 @@ impl Options {
             );
         }
 
-        let scrape_examples_options = ScrapeExamplesOptions::new(matches, &dcx);
+        let scrape_examples_options = ScrapeExamplesOptions::new(matches, dcx);
         let with_examples = matches.opt_strs("with-examples");
-        let call_locations = crate::scrape_examples::load_call_locations(with_examples, &dcx);
+        let call_locations = crate::scrape_examples::load_call_locations(with_examples, dcx);
 
         let unstable_features =
             rustc_feature::UnstableFeatures::from_environment(crate_name.as_deref());
@@ -847,7 +849,7 @@ fn parse_remap_path_prefix(
 }
 
 /// Prints deprecation warnings for deprecated options
-fn check_deprecated_options(matches: &getopts::Matches, dcx: &rustc_errors::DiagCtxt) {
+fn check_deprecated_options(matches: &getopts::Matches, dcx: DiagCtxtHandle<'_>) {
     let deprecated_flags = [];
 
     for &flag in deprecated_flags.iter() {
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index ce098a1bcfb..5d8e61f9fa0 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::emitter::{stderr_destination, DynEmitter, HumanEmitter};
 use rustc_errors::json::JsonEmitter;
-use rustc_errors::{codes::*, ErrorGuaranteed, TerminalUrl};
+use rustc_errors::{codes::*, DiagCtxtHandle, ErrorGuaranteed, TerminalUrl};
 use rustc_feature::UnstableFeatures;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
@@ -379,7 +379,7 @@ pub(crate) fn run_global_ctxt(
         );
     }
 
-    fn report_deprecated_attr(name: &str, dcx: &rustc_errors::DiagCtxt, sp: Span) {
+    fn report_deprecated_attr(name: &str, dcx: DiagCtxtHandle<'_>, sp: Span) {
         let mut msg =
             dcx.struct_span_warn(sp, format!("the `#![doc({name})]` attribute is deprecated"));
         msg.note(
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 81a7463deca..40cc4a9d441 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -7,7 +7,7 @@ pub(crate) use markdown::test as test_markdown;
 
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError};
+use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::CRATE_HIR_ID;
 use rustc_interface::interface;
@@ -90,10 +90,7 @@ fn get_doctest_dir() -> io::Result<TempDir> {
     TempFileBuilder::new().prefix("rustdoctest").tempdir()
 }
 
-pub(crate) fn run(
-    dcx: &rustc_errors::DiagCtxt,
-    options: RustdocOptions,
-) -> Result<(), ErrorGuaranteed> {
+pub(crate) fn run(dcx: DiagCtxtHandle<'_>, options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
     let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name;
 
     // See core::create_config for what's going on here.
diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs
index 599611407ed..74833c11362 100644
--- a/src/librustdoc/doctest/make.rs
+++ b/src/librustdoc/doctest/make.rs
@@ -229,7 +229,7 @@ fn check_for_main_and_extern_crate(
             // dcx. Any errors in the tests will be reported when the test file is compiled,
             // Note that we still need to cancel the errors above otherwise `Diag` will panic on
             // drop.
-            psess.dcx.reset_err_count();
+            psess.dcx().reset_err_count();
 
             (found_main, found_extern_crate, found_macro)
         })
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index 03ee042aa8d..62cdc0bd5a6 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -1,4 +1,5 @@
 use crate::html::markdown::{ErrorCodes, HeadingOffset, IdMap, Markdown, Playground};
+use rustc_errors::DiagCtxtHandle;
 use rustc_span::edition::Edition;
 use std::fs;
 use std::path::Path;
@@ -27,7 +28,7 @@ impl ExternalHtml {
         md_before_content: &[String],
         md_after_content: &[String],
         nightly_build: bool,
-        dcx: &rustc_errors::DiagCtxt,
+        dcx: DiagCtxtHandle<'_>,
         id_map: &mut IdMap,
         edition: Edition,
         playground: &Option<Playground>,
@@ -75,7 +76,7 @@ pub(crate) enum LoadStringError {
 
 pub(crate) fn load_string<P: AsRef<Path>>(
     file_path: P,
-    dcx: &rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> Result<String, LoadStringError> {
     let file_path = file_path.as_ref();
     let contents = match fs::read(file_path) {
@@ -98,7 +99,7 @@ pub(crate) fn load_string<P: AsRef<Path>>(
     }
 }
 
-fn load_external_files(names: &[String], dcx: &rustc_errors::DiagCtxt) -> Option<String> {
+fn load_external_files(names: &[String], dcx: DiagCtxtHandle<'_>) -> Option<String> {
     let mut out = String::new();
     for name in names {
         let Ok(s) = load_string(name, dcx) else { return None };
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index d6e715d48ea..fb4cd218b84 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -77,7 +77,7 @@ use std::io::{self, IsTerminal};
 use std::process;
 use std::sync::{atomic::AtomicBool, Arc};
 
-use rustc_errors::{ErrorGuaranteed, FatalError};
+use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, FatalError};
 use rustc_interface::interface;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::config::{make_crate_type_option, ErrorOutputType, RustcOptGroup};
@@ -670,7 +670,7 @@ fn usage(argv0: &str) {
 /// A result type used by several functions under `main()`.
 type MainResult = Result<(), ErrorGuaranteed>;
 
-pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult {
+pub(crate) fn wrap_return(dcx: DiagCtxtHandle<'_>, res: Result<(), String>) -> MainResult {
     match res {
         Ok(()) => dcx.has_errors().map_or(Ok(()), Err),
         Err(err) => Err(dcx.err(err)),
@@ -732,12 +732,13 @@ fn main_args(
         None => return Ok(()),
     };
 
-    let diag =
+    let dcx =
         core::new_dcx(options.error_format, None, options.diagnostic_width, &options.unstable_opts);
+    let dcx = dcx.handle();
 
     match (options.should_test, options.markdown_input()) {
-        (true, Some(_)) => return wrap_return(&diag, doctest::test_markdown(options)),
-        (true, None) => return doctest::run(&diag, options),
+        (true, Some(_)) => return wrap_return(dcx, doctest::test_markdown(options)),
+        (true, None) => return doctest::run(dcx, options),
         (false, Some(input)) => {
             let input = input.to_owned();
             let edition = options.edition;
@@ -747,7 +748,7 @@ fn main_args(
             // requires session globals and a thread pool, so we use
             // `run_compiler`.
             return wrap_return(
-                &diag,
+                dcx,
                 interface::run_compiler(config, |_compiler| {
                     markdown::render(&input, render_options, edition)
                 }),
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index e9b380fdeac..5a595e03953 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -7,6 +7,7 @@ use crate::formats::renderer::FormatRenderer;
 use crate::html::render::Context;
 
 use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::DiagCtxtHandle;
 use rustc_hir::{
     self as hir,
     intravisit::{self, Visitor},
@@ -38,7 +39,7 @@ pub(crate) struct ScrapeExamplesOptions {
 }
 
 impl ScrapeExamplesOptions {
-    pub(crate) fn new(matches: &getopts::Matches, dcx: &rustc_errors::DiagCtxt) -> Option<Self> {
+    pub(crate) fn new(matches: &getopts::Matches, dcx: DiagCtxtHandle<'_>) -> Option<Self> {
         let output_path = matches.opt_str("scrape-examples-output-path");
         let target_crates = matches.opt_strs("scrape-examples-target-crate");
         let scrape_tests = matches.opt_present("scrape-tests");
@@ -336,7 +337,7 @@ pub(crate) fn run(
 // options.
 pub(crate) fn load_call_locations(
     with_examples: Vec<String>,
-    dcx: &rustc_errors::DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> AllCallLocations {
     let mut all_calls: AllCallLocations = FxHashMap::default();
     for path in with_examples {
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 31d32e23f8e..2fa54a9cd81 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -5,7 +5,7 @@ use std::iter::Peekable;
 use std::path::Path;
 use std::str::Chars;
 
-use rustc_errors::DiagCtxt;
+use rustc_errors::DiagCtxtHandle;
 
 #[cfg(test)]
 mod tests;
@@ -236,7 +236,7 @@ pub(crate) fn get_differences(
 pub(crate) fn test_theme_against<P: AsRef<Path>>(
     f: &P,
     origin: &FxHashMap<String, CssPath>,
-    dcx: &DiagCtxt,
+    dcx: DiagCtxtHandle<'_>,
 ) -> (bool, Vec<String>) {
     let against = match fs::read_to_string(f)
         .map_err(|e| e.to_string())
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 06ae1723a03..4922c87b206 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -73,7 +73,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
                             predicate_pos,
                             bound_pos,
                         }),
-                        GenericBound::Outlives(_) => None,
+                        GenericBound::Outlives(_) | GenericBound::Use(..) => None,
                     })
                     .filter(|bound| !bound.trait_bound.span.from_expansion()),
             )
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index c70f5c2df84..fb43f7d80af 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -724,11 +724,8 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
         (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
         (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
-        (ImplTrait(_, lg, lc), ImplTrait(_, rg, rc)) => {
+        (ImplTrait(_, lg), ImplTrait(_, rg)) => {
             over(lg, rg, eq_generic_bound)
-                && both(lc, rc, |lc, rc| {
-                    over(lc.0.as_slice(), rc.0.as_slice(), eq_precise_capture)
-                })
         },
         (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index f79da26964f..6117e76897f 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -180,12 +180,12 @@ pub fn main() {
 
     rustc_driver::init_rustc_env_logger(&early_dcx);
 
-    let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |handler| {
+    let using_internal_features = rustc_driver::install_ice_hook(BUG_REPORT_URL, |dcx| {
         // FIXME: this macro calls unwrap internally but is called in a panicking context!  It's not
         // as simple as moving the call from the hook to main, because `install_ice_hook` doesn't
         // accept a generic closure.
         let version_info = rustc_tools_util::get_version_info!();
-        handler.note(format!("Clippy version: {version_info}"));
+        dcx.handle().note(format!("Clippy version: {version_info}"));
     });
 
     exit(rustc_driver::catch_with_exit_code(move || {
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 47f0913acce..139c81a9d40 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -569,7 +569,7 @@ pub fn report_msg<'tcx>(
         let is_local = machine.is_local(frame_info);
         // No span for non-local frames and the first frame (which is the error site).
         if is_local && idx > 0 {
-            err.subdiagnostic(err.dcx, frame_info.as_note(machine.tcx));
+            err.subdiagnostic(frame_info.as_note(machine.tcx));
         } else {
             let sm = sess.source_map();
             let span = sm.span_to_embeddable_string(frame_info.span);
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index a358ae83de1..f4c101cf81c 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -30,7 +30,8 @@ pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
 pub use clang::{clang, Clang};
 pub use diff::{diff, Diff};
 pub use llvm::{
-    llvm_filecheck, llvm_profdata, llvm_readobj, LlvmFilecheck, LlvmProfdata, LlvmReadobj,
+    llvm_filecheck, llvm_objdump, llvm_profdata, llvm_readobj, LlvmFilecheck, LlvmObjdump,
+    LlvmProfdata, LlvmReadobj,
 };
 pub use run::{cmd, run, run_fail, run_with_args};
 pub use rustc::{aux_build, rustc, Rustc};
@@ -303,6 +304,34 @@ pub fn set_host_rpath(cmd: &mut Command) {
     });
 }
 
+/// Read the contents of a file that cannot simply be read by
+/// read_to_string, due to invalid utf8 data, then assert that it contains `expected`.
+#[track_caller]
+pub fn invalid_utf8_contains<P: AsRef<Path>>(path: P, expected: &str) {
+    let buffer = fs_wrapper::read(path.as_ref());
+    if !String::from_utf8_lossy(&buffer).contains(expected) {
+        eprintln!("=== FILE CONTENTS (LOSSY) ===");
+        eprintln!("{}", String::from_utf8_lossy(&buffer));
+        eprintln!("=== SPECIFIED TEXT ===");
+        eprintln!("{}", expected);
+        panic!("specified text was not found in file");
+    }
+}
+
+/// Read the contents of a file that cannot simply be read by
+/// read_to_string, due to invalid utf8 data, then assert that it does not contain `expected`.
+#[track_caller]
+pub fn invalid_utf8_not_contains<P: AsRef<Path>>(path: P, expected: &str) {
+    let buffer = fs_wrapper::read(path.as_ref());
+    if String::from_utf8_lossy(&buffer).contains(expected) {
+        eprintln!("=== FILE CONTENTS (LOSSY) ===");
+        eprintln!("{}", String::from_utf8_lossy(&buffer));
+        eprintln!("=== SPECIFIED TEXT ===");
+        eprintln!("{}", expected);
+        panic!("specified text was unexpectedly found in file");
+    }
+}
+
 /// Copy a directory into another.
 pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) {
     fn copy_dir_all_inner(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs
index 664093e072d..fe4131819ba 100644
--- a/src/tools/run-make-support/src/llvm.rs
+++ b/src/tools/run-make-support/src/llvm.rs
@@ -23,6 +23,12 @@ pub fn llvm_filecheck() -> LlvmFilecheck {
     LlvmFilecheck::new()
 }
 
+/// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available
+/// at `$LLVM_BIN_DIR/llvm-objdump`.
+pub fn llvm_objdump() -> LlvmObjdump {
+    LlvmObjdump::new()
+}
+
 /// A `llvm-readobj` invocation builder.
 #[derive(Debug)]
 #[must_use]
@@ -44,9 +50,17 @@ pub struct LlvmFilecheck {
     cmd: Command,
 }
 
+/// A `llvm-objdump` invocation builder.
+#[derive(Debug)]
+#[must_use]
+pub struct LlvmObjdump {
+    cmd: Command,
+}
+
 crate::impl_common_helpers!(LlvmReadobj);
 crate::impl_common_helpers!(LlvmProfdata);
 crate::impl_common_helpers!(LlvmFilecheck);
+crate::impl_common_helpers!(LlvmObjdump);
 
 /// Generate the path to the bin directory of LLVM.
 #[must_use]
@@ -131,3 +145,19 @@ impl LlvmFilecheck {
         self
     }
 }
+
+impl LlvmObjdump {
+    /// Construct a new `llvm-objdump` invocation. This assumes that `llvm-objdump` is available
+    /// at `$LLVM_BIN_DIR/llvm-objdump`.
+    pub fn new() -> Self {
+        let llvm_objdump = llvm_bin_dir().join("llvm-objdump");
+        let cmd = Command::new(llvm_objdump);
+        Self { cmd }
+    }
+
+    /// Provide an input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+}
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index 5fc988e4319..b91d203d531 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -67,7 +67,7 @@ fn parse_cfg_if_inner<'a>(
                 Ok(None) => continue,
                 Err(err) => {
                     err.cancel();
-                    parser.psess.dcx.reset_err_count();
+                    parser.psess.dcx().reset_err_count();
                     return Err(
                         "Expected item inside cfg_if block, but failed to parse it as an item",
                     );
diff --git a/src/tools/rustfmt/src/parse/macros/lazy_static.rs b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
index badd9569950..7026935294a 100644
--- a/src/tools/rustfmt/src/parse/macros/lazy_static.rs
+++ b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
@@ -16,8 +16,8 @@ pub(crate) fn parse_lazy_static(
         ($method:ident $(,)* $($arg:expr),* $(,)*) => {
             match parser.$method($($arg,)*) {
                 Ok(val) => {
-                    if parser.psess.dcx.has_errors().is_some() {
-                        parser.psess.dcx.reset_err_count();
+                    if parser.psess.dcx().has_errors().is_some() {
+                        parser.psess.dcx().reset_err_count();
                         return None;
                     } else {
                         val
@@ -25,7 +25,7 @@ pub(crate) fn parse_lazy_static(
                 }
                 Err(err) => {
                     err.cancel();
-                    parser.psess.dcx.reset_err_count();
+                    parser.psess.dcx().reset_err_count();
                     return None;
                 }
             }
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index cbcc0b2d636..89169e10715 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -29,8 +29,8 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
             if Parser::nonterminal_may_begin_with($nt_kind, &cloned_parser.token) {
                 match $try_parse(&mut cloned_parser) {
                     Ok(x) => {
-                        if parser.psess.dcx.has_errors().is_some() {
-                            parser.psess.dcx.reset_err_count();
+                        if parser.psess.dcx().has_errors().is_some() {
+                            parser.psess.dcx().reset_err_count();
                         } else {
                             // Parsing succeeded.
                             *parser = cloned_parser;
@@ -39,7 +39,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
                     }
                     Err(e) => {
                         e.cancel();
-                        parser.psess.dcx.reset_err_count();
+                        parser.psess.dcx().reset_err_count();
                     }
                 }
             }
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 1a39d212386..f4fbabaf6c9 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -210,7 +210,9 @@ impl ParseSess {
             rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(),
             false,
         );
-        self.raw_psess.dcx.make_silent(fallback_bundle, None, false);
+        self.raw_psess
+            .dcx()
+            .make_silent(fallback_bundle, None, false);
     }
 
     pub(crate) fn span_to_filename(&self, span: Span) -> FileName {
@@ -286,11 +288,11 @@ impl ParseSess {
     }
 
     pub(super) fn has_errors(&self) -> bool {
-        self.raw_psess.dcx.has_errors().is_some()
+        self.raw_psess.dcx().has_errors().is_some()
     }
 
     pub(super) fn reset_errors(&self) {
-        self.raw_psess.dcx.reset_err_count();
+        self.raw_psess.dcx().reset_err_count();
     }
 }
 
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index 4aaf7fdb27f..28911f8af1d 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -181,6 +181,7 @@ impl Spanned for ast::GenericBound {
         match *self {
             ast::GenericBound::Trait(ref ptr, _) => ptr.span,
             ast::GenericBound::Outlives(ref l) => l.ident.span,
+            ast::GenericBound::Use(_, span) => span,
         }
     }
 }
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 7d14d9e727a..c2c192738c9 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -563,6 +563,8 @@ impl Rewrite for ast::GenericBound {
                     .map(|s| if has_paren { format!("({})", s) } else { s })
             }
             ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape),
+            // FIXME(precise_capturing): Should implement formatting before stabilization.
+            ast::GenericBound::Use(..) => None,
         }
     }
 }
@@ -843,11 +845,7 @@ impl Rewrite for ast::Ty {
                 rewrite_macro(mac, None, context, shape, MacroPosition::Expression)
             }
             ast::TyKind::ImplicitSelf => Some(String::from("")),
-            ast::TyKind::ImplTrait(_, ref it, ref captures) => {
-                // FIXME(precise_capturing): Implement formatting.
-                if captures.is_some() {
-                    return None;
-                }
+            ast::TyKind::ImplTrait(_, ref it) => {
                 // Empty trait is not a parser error.
                 if it.is_empty() {
                     return Some("impl".to_owned());
@@ -932,6 +930,8 @@ fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool {
     let is_trait = |b: &ast::GenericBound| match b {
         ast::GenericBound::Outlives(..) => false,
         ast::GenericBound::Trait(..) => true,
+        // FIXME(precise_capturing): This ordering fn should be reworked.
+        ast::GenericBound::Use(..) => false,
     };
     let is_lifetime = |b: &ast::GenericBound| !is_trait(b);
     let last_trait_index = generic_bounds.iter().rposition(is_trait);
@@ -966,6 +966,8 @@ fn join_bounds_inner(
     let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b {
         ast::GenericBound::Outlives(..) => true,
         ast::GenericBound::Trait(..) => last_line_extendable(s),
+        // FIXME(precise_capturing): This ordering fn should be reworked.
+        ast::GenericBound::Use(..) => true,
     };
 
     // Whether a GenericBound item is a PathSegment segment that includes internal array
@@ -1110,8 +1112,7 @@ fn join_bounds_inner(
 
 pub(crate) fn opaque_ty(ty: &Option<ptr::P<ast::Ty>>) -> Option<&ast::GenericBounds> {
     ty.as_ref().and_then(|t| match &t.kind {
-        // FIXME(precise_capturing): Implement support here
-        ast::TyKind::ImplTrait(_, bounds, _) => Some(bounds),
+        ast::TyKind::ImplTrait(_, bounds) => Some(bounds),
         _ => None,
     })
 }
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 895798d7c1f..12d5f3576ca 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,4 +1,3 @@
-run-make/allocator-shim-circular-deps/Makefile
 run-make/archive-duplicate-names/Makefile
 run-make/atomic-lock-free/Makefile
 run-make/branch-protection-check-IBT/Makefile
@@ -35,12 +34,10 @@ run-make/emit-shared-files/Makefile
 run-make/emit-stack-sizes/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/env-dep-info/Makefile
-run-make/error-found-staticlib-instead-crate/Makefile
 run-make/error-writing-dependencies/Makefile
 run-make/export-executable-symbols/Makefile
 run-make/extern-diff-internal-name/Makefile
 run-make/extern-flag-disambiguates/Makefile
-run-make/extern-flag-fun/Makefile
 run-make/extern-flag-pathless/Makefile
 run-make/extern-flag-rename-transitive/Makefile
 run-make/extern-fn-explicit-align/Makefile
@@ -64,14 +61,11 @@ run-make/glibc-staticlib-args/Makefile
 run-make/include_bytes_deps/Makefile
 run-make/incr-add-rust-src-component/Makefile
 run-make/incr-foreign-head-span/Makefile
-run-make/incremental-debugger-visualizer/Makefile
-run-make/incremental-session-fail/Makefile
 run-make/inline-always-many-cgu/Makefile
 run-make/interdependent-c-libraries/Makefile
 run-make/intrinsic-unreachable/Makefile
 run-make/invalid-library/Makefile
 run-make/invalid-so/Makefile
-run-make/invalid-staticlib/Makefile
 run-make/issue-107094/Makefile
 run-make/issue-109934-lto-debuginfo/Makefile
 run-make/issue-14698/Makefile
@@ -90,7 +84,6 @@ run-make/issue-40535/Makefile
 run-make/issue-47384/Makefile
 run-make/issue-47551/Makefile
 run-make/issue-51671/Makefile
-run-make/issue-64153/Makefile
 run-make/issue-68794-textrel-on-minimal-lib/Makefile
 run-make/issue-69368/Makefile
 run-make/issue-83045/Makefile
@@ -135,18 +128,13 @@ run-make/missing-crate-dependency/Makefile
 run-make/mixing-libs/Makefile
 run-make/msvc-opt-minsize/Makefile
 run-make/native-link-modifier-bundle/Makefile
-run-make/native-link-modifier-verbatim-linker/Makefile
-run-make/native-link-modifier-verbatim-rustc/Makefile
 run-make/native-link-modifier-whole-archive/Makefile
 run-make/no-alloc-shim/Makefile
 run-make/no-builtins-attribute/Makefile
-run-make/no-builtins-lto/Makefile
 run-make/no-duplicate-libs/Makefile
 run-make/obey-crate-type-flag/Makefile
 run-make/optimization-remarks-dir-pgo/Makefile
 run-make/optimization-remarks-dir/Makefile
-run-make/output-filename-conflicts-with-directory/Makefile
-run-make/output-filename-overwrites-input/Makefile
 run-make/output-type-permutations/Makefile
 run-make/override-aliased-flags/Makefile
 run-make/overwrite-input/Makefile
@@ -196,8 +184,6 @@ run-make/rustdoc-io-error/Makefile
 run-make/sanitizer-cdylib-link/Makefile
 run-make/sanitizer-dylib-link/Makefile
 run-make/sanitizer-staticlib-link/Makefile
-run-make/separate-link-fail/Makefile
-run-make/separate-link/Makefile
 run-make/sepcomp-cci-copies/Makefile
 run-make/sepcomp-inlining/Makefile
 run-make/sepcomp-separate/Makefile
@@ -231,7 +217,6 @@ run-make/unknown-mod-stdin/Makefile
 run-make/unstable-flag-required/Makefile
 run-make/use-suggestions-rust-2018/Makefile
 run-make/used-cdylib-macos/Makefile
-run-make/used/Makefile
 run-make/volatile-intrinsics/Makefile
 run-make/wasm-exceptions-nostd/Makefile
 run-make/wasm-override-linker/Makefile
diff --git a/tests/coverage/attr/nested.cov-map b/tests/coverage/attr/nested.cov-map
new file mode 100644
index 00000000000..a613bb7f8cd
--- /dev/null
+++ b/tests/coverage/attr/nested.cov-map
@@ -0,0 +1,100 @@
+Function name: <<<nested::MyOuter as nested::MyTrait>::trait_method::MyMiddle as nested::MyTrait>::trait_method::MyInner as nested::MyTrait>::trait_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 39, 15, 02, 16]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 57, 21) to (start + 2, 22)
+
+Function name: <<<nested::MyOuter>::outer_method::MyMiddle>::middle_method::MyInner>::inner_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 23, 15, 02, 16]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 35, 21) to (start + 2, 22)
+
+Function name: <<nested::MyOuter as nested::MyTrait>::trait_method::MyMiddle as nested::MyTrait>::trait_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 36, 0d, 08, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 54, 13) to (start + 8, 14)
+
+Function name: <<nested::MyOuter>::outer_method::MyMiddle>::middle_method (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 20, 0d, 08, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 32, 13) to (start + 8, 14)
+
+Function name: nested::closure_expr
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 44, 01, 01, 0f, 01, 0b, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 68, 1) to (start + 1, 15)
+- Code(Counter(0)) at (prev + 11, 5) to (start + 1, 2)
+
+Function name: nested::closure_expr::{closure#0}::{closure#0} (unused)
+Raw bytes (14): 0x[01, 01, 00, 02, 00, 47, 1a, 01, 17, 00, 04, 0d, 01, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 71, 26) to (start + 1, 23)
+- Code(Zero) at (prev + 4, 13) to (start + 1, 10)
+
+Function name: nested::closure_expr::{closure#0}::{closure#0}::{closure#0} (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 48, 1d, 02, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 72, 29) to (start + 2, 14)
+
+Function name: nested::closure_tail
+Raw bytes (14): 0x[01, 01, 00, 02, 01, 53, 01, 01, 0f, 01, 11, 05, 01, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 83, 1) to (start + 1, 15)
+- Code(Counter(0)) at (prev + 17, 5) to (start + 1, 2)
+
+Function name: nested::closure_tail::{closure#0}::{closure#0} (unused)
+Raw bytes (14): 0x[01, 01, 00, 02, 00, 58, 14, 01, 1f, 00, 06, 15, 01, 12]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Zero) at (prev + 88, 20) to (start + 1, 31)
+- Code(Zero) at (prev + 6, 21) to (start + 1, 18)
+
+Function name: nested::closure_tail::{closure#0}::{closure#0}::{closure#0} (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 5a, 1c, 02, 1a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 90, 28) to (start + 2, 26)
+
+Function name: nested::outer_fn::middle_fn (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 11, 05, 05, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 17, 5) to (start + 5, 6)
+
+Function name: nested::outer_fn::middle_fn::inner_fn (unused)
+Raw bytes (9): 0x[01, 01, 00, 01, 00, 12, 09, 02, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Zero) at (prev + 18, 9) to (start + 2, 10)
+
diff --git a/tests/coverage/attr/nested.coverage b/tests/coverage/attr/nested.coverage
new file mode 100644
index 00000000000..13129572aec
--- /dev/null
+++ b/tests/coverage/attr/nested.coverage
@@ -0,0 +1,111 @@
+   LL|       |#![feature(coverage_attribute, stmt_expr_attributes)]
+   LL|       |//@ edition: 2021
+   LL|       |
+   LL|       |// Demonstrates the interaction between #[coverage(off)] and various kinds of
+   LL|       |// nested function.
+   LL|       |
+   LL|       |// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+   LL|       |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+   LL|       |// its lines can still be marked with misleading execution counts from its enclosing
+   LL|       |// function.
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn do_stuff() {}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn outer_fn() {
+   LL|      0|    fn middle_fn() {
+   LL|      0|        fn inner_fn() {
+   LL|      0|            do_stuff();
+   LL|      0|        }
+   LL|      0|        do_stuff();
+   LL|      0|    }
+   LL|       |    do_stuff();
+   LL|       |}
+   LL|       |
+   LL|       |struct MyOuter;
+   LL|       |impl MyOuter {
+   LL|       |    #[coverage(off)]
+   LL|       |    fn outer_method(&self) {
+   LL|       |        struct MyMiddle;
+   LL|       |        impl MyMiddle {
+   LL|      0|            fn middle_method(&self) {
+   LL|      0|                struct MyInner;
+   LL|      0|                impl MyInner {
+   LL|      0|                    fn inner_method(&self) {
+   LL|      0|                        do_stuff();
+   LL|      0|                    }
+   LL|      0|                }
+   LL|      0|                do_stuff();
+   LL|      0|            }
+   LL|       |        }
+   LL|       |        do_stuff();
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |trait MyTrait {
+   LL|       |    fn trait_method(&self);
+   LL|       |}
+   LL|       |impl MyTrait for MyOuter {
+   LL|       |    #[coverage(off)]
+   LL|       |    fn trait_method(&self) {
+   LL|       |        struct MyMiddle;
+   LL|       |        impl MyTrait for MyMiddle {
+   LL|      0|            fn trait_method(&self) {
+   LL|      0|                struct MyInner;
+   LL|      0|                impl MyTrait for MyInner {
+   LL|      0|                    fn trait_method(&self) {
+   LL|      0|                        do_stuff();
+   LL|      0|                    }
+   LL|      0|                }
+   LL|      0|                do_stuff();
+   LL|      0|            }
+   LL|       |        }
+   LL|       |        do_stuff();
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn closure_expr() {
+   LL|      1|    let _outer = #[coverage(off)]
+   LL|       |    || {
+   LL|      0|        let _middle = || {
+   LL|      0|            let _inner = || {
+   LL|      0|                do_stuff();
+   LL|      0|            };
+   LL|      0|            do_stuff();
+   LL|      0|        };
+   LL|       |        do_stuff();
+   LL|       |    };
+   LL|      1|    do_stuff();
+   LL|      1|}
+   LL|       |
+   LL|       |// This syntax is allowed, even without #![feature(stmt_expr_attributes)].
+   LL|      1|fn closure_tail() {
+   LL|      1|    let _outer = {
+   LL|       |        #[coverage(off)]
+   LL|       |        || {
+   LL|       |            let _middle = {
+   LL|      0|                || {
+   LL|      0|                    let _inner = {
+   LL|      0|                        || {
+   LL|      0|                            do_stuff();
+   LL|      0|                        }
+   LL|       |                    };
+   LL|      0|                    do_stuff();
+   LL|      0|                }
+   LL|       |            };
+   LL|       |            do_stuff();
+   LL|       |        }
+   LL|       |    };
+   LL|      1|    do_stuff();
+   LL|      1|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    outer_fn();
+   LL|       |    MyOuter.outer_method();
+   LL|       |    MyOuter.trait_method();
+   LL|       |    closure_expr();
+   LL|       |    closure_tail();
+   LL|       |}
+
diff --git a/tests/coverage/attr/nested.rs b/tests/coverage/attr/nested.rs
new file mode 100644
index 00000000000..c7ff835f44f
--- /dev/null
+++ b/tests/coverage/attr/nested.rs
@@ -0,0 +1,110 @@
+#![feature(coverage_attribute, stmt_expr_attributes)]
+//@ edition: 2021
+
+// Demonstrates the interaction between #[coverage(off)] and various kinds of
+// nested function.
+
+// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+// its lines can still be marked with misleading execution counts from its enclosing
+// function.
+
+#[coverage(off)]
+fn do_stuff() {}
+
+#[coverage(off)]
+fn outer_fn() {
+    fn middle_fn() {
+        fn inner_fn() {
+            do_stuff();
+        }
+        do_stuff();
+    }
+    do_stuff();
+}
+
+struct MyOuter;
+impl MyOuter {
+    #[coverage(off)]
+    fn outer_method(&self) {
+        struct MyMiddle;
+        impl MyMiddle {
+            fn middle_method(&self) {
+                struct MyInner;
+                impl MyInner {
+                    fn inner_method(&self) {
+                        do_stuff();
+                    }
+                }
+                do_stuff();
+            }
+        }
+        do_stuff();
+    }
+}
+
+trait MyTrait {
+    fn trait_method(&self);
+}
+impl MyTrait for MyOuter {
+    #[coverage(off)]
+    fn trait_method(&self) {
+        struct MyMiddle;
+        impl MyTrait for MyMiddle {
+            fn trait_method(&self) {
+                struct MyInner;
+                impl MyTrait for MyInner {
+                    fn trait_method(&self) {
+                        do_stuff();
+                    }
+                }
+                do_stuff();
+            }
+        }
+        do_stuff();
+    }
+}
+
+fn closure_expr() {
+    let _outer = #[coverage(off)]
+    || {
+        let _middle = || {
+            let _inner = || {
+                do_stuff();
+            };
+            do_stuff();
+        };
+        do_stuff();
+    };
+    do_stuff();
+}
+
+// This syntax is allowed, even without #![feature(stmt_expr_attributes)].
+fn closure_tail() {
+    let _outer = {
+        #[coverage(off)]
+        || {
+            let _middle = {
+                || {
+                    let _inner = {
+                        || {
+                            do_stuff();
+                        }
+                    };
+                    do_stuff();
+                }
+            };
+            do_stuff();
+        }
+    };
+    do_stuff();
+}
+
+#[coverage(off)]
+fn main() {
+    outer_fn();
+    MyOuter.outer_method();
+    MyOuter.trait_method();
+    closure_expr();
+    closure_tail();
+}
diff --git a/tests/coverage/attr/off-on-sandwich.cov-map b/tests/coverage/attr/off-on-sandwich.cov-map
new file mode 100644
index 00000000000..72b96420cb5
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.cov-map
@@ -0,0 +1,32 @@
+Function name: off_on_sandwich::dense_a::dense_b
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 14, 05, 07, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 20, 5) to (start + 7, 6)
+
+Function name: off_on_sandwich::sparse_a::sparse_b
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 22, 05, 10, 06]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 34, 5) to (start + 16, 6)
+
+Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 26, 09, 0b, 0a]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 38, 9) to (start + 11, 10)
+
+Function name: off_on_sandwich::sparse_a::sparse_b::sparse_c::sparse_d
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 29, 0d, 07, 0e]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 41, 13) to (start + 7, 14)
+
diff --git a/tests/coverage/attr/off-on-sandwich.coverage b/tests/coverage/attr/off-on-sandwich.coverage
new file mode 100644
index 00000000000..e831b0e926e
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.coverage
@@ -0,0 +1,58 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |
+   LL|       |// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]`
+   LL|       |// in nested functions.
+   LL|       |
+   LL|       |// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+   LL|       |// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+   LL|       |// its lines can still be marked with misleading execution counts from its enclosing
+   LL|       |// function.
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn do_stuff() {}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn dense_a() {
+   LL|       |    dense_b();
+   LL|       |    dense_b();
+   LL|       |    #[coverage(on)]
+   LL|      2|    fn dense_b() {
+   LL|      2|        dense_c();
+   LL|      2|        dense_c();
+   LL|      2|        #[coverage(off)]
+   LL|      2|        fn dense_c() {
+   LL|      2|            do_stuff();
+   LL|      2|        }
+   LL|      2|    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn sparse_a() {
+   LL|       |    sparse_b();
+   LL|       |    sparse_b();
+   LL|      2|    fn sparse_b() {
+   LL|      2|        sparse_c();
+   LL|      2|        sparse_c();
+   LL|      2|        #[coverage(on)]
+   LL|      4|        fn sparse_c() {
+   LL|      4|            sparse_d();
+   LL|      4|            sparse_d();
+   LL|      8|            fn sparse_d() {
+   LL|      8|                sparse_e();
+   LL|      8|                sparse_e();
+   LL|      8|                #[coverage(off)]
+   LL|      8|                fn sparse_e() {
+   LL|      8|                    do_stuff();
+   LL|      8|                }
+   LL|      8|            }
+   LL|      4|        }
+   LL|      2|    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    dense_a();
+   LL|       |    sparse_a();
+   LL|       |}
+
diff --git a/tests/coverage/attr/off-on-sandwich.rs b/tests/coverage/attr/off-on-sandwich.rs
new file mode 100644
index 00000000000..6b21b180223
--- /dev/null
+++ b/tests/coverage/attr/off-on-sandwich.rs
@@ -0,0 +1,57 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+
+// Demonstrates the interaction of `#[coverage(off)]` and `#[coverage(on)]`
+// in nested functions.
+
+// FIXME(#126625): Coverage attributes should apply recursively to nested functions.
+// FIXME(#126626): When an inner (non-closure) function has `#[coverage(off)]`,
+// its lines can still be marked with misleading execution counts from its enclosing
+// function.
+
+#[coverage(off)]
+fn do_stuff() {}
+
+#[coverage(off)]
+fn dense_a() {
+    dense_b();
+    dense_b();
+    #[coverage(on)]
+    fn dense_b() {
+        dense_c();
+        dense_c();
+        #[coverage(off)]
+        fn dense_c() {
+            do_stuff();
+        }
+    }
+}
+
+#[coverage(off)]
+fn sparse_a() {
+    sparse_b();
+    sparse_b();
+    fn sparse_b() {
+        sparse_c();
+        sparse_c();
+        #[coverage(on)]
+        fn sparse_c() {
+            sparse_d();
+            sparse_d();
+            fn sparse_d() {
+                sparse_e();
+                sparse_e();
+                #[coverage(off)]
+                fn sparse_e() {
+                    do_stuff();
+                }
+            }
+        }
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    dense_a();
+    sparse_a();
+}
diff --git a/tests/coverage/branch/no-mir-spans.cov-map b/tests/coverage/branch/no-mir-spans.cov-map
new file mode 100644
index 00000000000..cb19211913f
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.cov-map
@@ -0,0 +1,52 @@
+Function name: no_mir_spans::while_cond
+Raw bytes (16): 0x[01, 01, 00, 02, 01, 10, 01, 00, 11, 20, 05, 09, 04, 0b, 00, 10]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 16, 1) to (start + 0, 17)
+- Branch { true: Counter(1), false: Counter(2) } at (prev + 4, 11) to (start + 0, 16)
+    true  = c1
+    false = c2
+
+Function name: no_mir_spans::while_cond_not
+Raw bytes (16): 0x[01, 01, 00, 02, 01, 19, 01, 00, 15, 20, 09, 05, 04, 0b, 00, 14]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 2
+- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 21)
+- Branch { true: Counter(2), false: Counter(1) } at (prev + 4, 11) to (start + 0, 20)
+    true  = c2
+    false = c1
+
+Function name: no_mir_spans::while_op_and
+Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 22, 01, 00, 13, 20, 09, 05, 05, 0b, 00, 10, 20, 02, 0d, 00, 14, 00, 19]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 1
+- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 3
+- Code(Counter(0)) at (prev + 34, 1) to (start + 0, 19)
+- Branch { true: Counter(2), false: Counter(1) } at (prev + 5, 11) to (start + 0, 16)
+    true  = c2
+    false = c1
+- Branch { true: Expression(0, Sub), false: Counter(3) } at (prev + 0, 20) to (start + 0, 25)
+    true  = (c2 - c3)
+    false = c3
+
+Function name: no_mir_spans::while_op_or
+Raw bytes (25): 0x[01, 01, 01, 09, 0d, 03, 01, 2d, 01, 00, 12, 20, 05, 09, 05, 0b, 00, 10, 20, 0d, 02, 00, 14, 00, 19]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 1
+- expression 0 operands: lhs = Counter(2), rhs = Counter(3)
+Number of file 0 mappings: 3
+- Code(Counter(0)) at (prev + 45, 1) to (start + 0, 18)
+- Branch { true: Counter(1), false: Counter(2) } at (prev + 5, 11) to (start + 0, 16)
+    true  = c1
+    false = c2
+- Branch { true: Counter(3), false: Expression(0, Sub) } at (prev + 0, 20) to (start + 0, 25)
+    true  = c3
+    false = (c2 - c3)
+
diff --git a/tests/coverage/branch/no-mir-spans.coverage b/tests/coverage/branch/no-mir-spans.coverage
new file mode 100644
index 00000000000..2cae98ed3ff
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.coverage
@@ -0,0 +1,77 @@
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |//@ edition: 2021
+   LL|       |//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
+   LL|       |//@ llvm-cov-flags: --show-branches=count
+   LL|       |
+   LL|       |// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
+   LL|       |// The actual code below is just some non-trivial code copied from another test
+   LL|       |// (`while.rs`), and has no particular significance.
+   LL|       |
+   LL|       |macro_rules! no_merge {
+   LL|       |    () => {
+   LL|       |        for _ in 0..1 {}
+   LL|       |    };
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_cond() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    while a > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 8, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_cond_not() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    while !(a == 0) {
+  ------------------
+  |  Branch (LL:11): [True: 8, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_op_and() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 8;
+   LL|       |    let mut b = 4;
+   LL|       |    while a > 0 && b > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 5, False: 0]
+  |  Branch (LL:20): [True: 4, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |        b -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|      1|fn while_op_or() {
+   LL|       |    no_merge!();
+   LL|       |
+   LL|       |    let mut a = 4;
+   LL|       |    let mut b = 8;
+   LL|       |    while a > 0 || b > 0 {
+  ------------------
+  |  Branch (LL:11): [True: 4, False: 5]
+  |  Branch (LL:20): [True: 4, False: 1]
+  ------------------
+   LL|       |        a -= 1;
+   LL|       |        b -= 1;
+   LL|       |    }
+   LL|       |}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    while_cond();
+   LL|       |    while_cond_not();
+   LL|       |    while_op_and();
+   LL|       |    while_op_or();
+   LL|       |}
+
diff --git a/tests/coverage/branch/no-mir-spans.rs b/tests/coverage/branch/no-mir-spans.rs
new file mode 100644
index 00000000000..acb268f2d45
--- /dev/null
+++ b/tests/coverage/branch/no-mir-spans.rs
@@ -0,0 +1,62 @@
+#![feature(coverage_attribute)]
+//@ edition: 2021
+//@ compile-flags: -Zcoverage-options=branch,no-mir-spans
+//@ llvm-cov-flags: --show-branches=count
+
+// Tests the behaviour of the `-Zcoverage-options=no-mir-spans` debugging flag.
+// The actual code below is just some non-trivial code copied from another test
+// (`while.rs`), and has no particular significance.
+
+macro_rules! no_merge {
+    () => {
+        for _ in 0..1 {}
+    };
+}
+
+fn while_cond() {
+    no_merge!();
+
+    let mut a = 8;
+    while a > 0 {
+        a -= 1;
+    }
+}
+
+fn while_cond_not() {
+    no_merge!();
+
+    let mut a = 8;
+    while !(a == 0) {
+        a -= 1;
+    }
+}
+
+fn while_op_and() {
+    no_merge!();
+
+    let mut a = 8;
+    let mut b = 4;
+    while a > 0 && b > 0 {
+        a -= 1;
+        b -= 1;
+    }
+}
+
+fn while_op_or() {
+    no_merge!();
+
+    let mut a = 4;
+    let mut b = 8;
+    while a > 0 || b > 0 {
+        a -= 1;
+        b -= 1;
+    }
+}
+
+#[coverage(off)]
+fn main() {
+    while_cond();
+    while_cond_not();
+    while_op_and();
+    while_op_or();
+}
diff --git a/tests/run-make/allocator-shim-circular-deps/Makefile b/tests/run-make/allocator-shim-circular-deps/Makefile
deleted file mode 100644
index f667e2e2ec2..00000000000
--- a/tests/run-make/allocator-shim-circular-deps/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-# This test is designed to intentionally introduce a circular dependency scenario to check that a specific compiler bug doesn't make a resurgence.
-# The bug in question arose when at least one crate required a global allocator, and that crate was placed after the one defining it in the linker order. 
-# The generated symbols.o should not result in any linker errors.
-# See https://github.com/rust-lang/rust/issues/112715
-
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	rm -rf $(TMPDIR) && mkdir $(TMPDIR)
-	$(RUSTC) my_lib.rs
-	$(RUSTC) main.rs --test --extern my_lib=$(TMPDIR)/libmy_lib.rlib
diff --git a/tests/run-make/allocator-shim-circular-deps/rmake.rs b/tests/run-make/allocator-shim-circular-deps/rmake.rs
new file mode 100644
index 00000000000..7d6b0bd204a
--- /dev/null
+++ b/tests/run-make/allocator-shim-circular-deps/rmake.rs
@@ -0,0 +1,16 @@
+// This test is designed to intentionally introduce a circular dependency scenario to check
+// that a specific compiler bug doesn't make a resurgence.
+// The bug in question arose when at least one crate
+// required a global allocator, and that crate was placed after
+// the one defining it in the linker order.
+// The generated symbols.o should not result in any linker errors.
+// See https://github.com/rust-lang/rust/issues/112715
+
+//@ ignore-cross-compile
+
+use run_make_support::{rust_lib_name, rustc};
+
+fn main() {
+    rustc().input("my_lib.rs").run();
+    rustc().input("main.rs").arg("--test").extern_("my_lib", rust_lib_name("my_lib")).run();
+}
diff --git a/tests/run-make/error-found-staticlib-instead-crate/Makefile b/tests/run-make/error-found-staticlib-instead-crate/Makefile
deleted file mode 100644
index 0eae41d720c..00000000000
--- a/tests/run-make/error-found-staticlib-instead-crate/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-include ../tools.mk
-
-all:
-	$(RUSTC) foo.rs --crate-type staticlib
-	$(RUSTC) bar.rs 2>&1 | $(CGREP) "found staticlib"
diff --git a/tests/run-make/error-found-staticlib-instead-crate/rmake.rs b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs
new file mode 100644
index 00000000000..8c707092b7e
--- /dev/null
+++ b/tests/run-make/error-found-staticlib-instead-crate/rmake.rs
@@ -0,0 +1,11 @@
+// When rustc is looking for a crate but is given a staticlib instead,
+// the error message should be helpful and indicate precisely the cause
+// of the compilation failure.
+// See https://github.com/rust-lang/rust/pull/21978
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc().input("foo.rs").crate_type("staticlib").run();
+    rustc().input("bar.rs").run_fail().assert_stderr_contains("found staticlib");
+}
diff --git a/tests/run-make/extern-flag-fun/Makefile b/tests/run-make/extern-flag-fun/Makefile
deleted file mode 100644
index 687cdfd7675..00000000000
--- a/tests/run-make/extern-flag-fun/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	$(RUSTC) bar.rs --crate-type=rlib
-	$(RUSTC) bar.rs --crate-type=rlib -C extra-filename=-a
-	$(RUSTC) bar-alt.rs --crate-type=rlib
-	$(RUSTC) foo.rs --extern bar=no-exist && exit 1 || exit 0
-	$(RUSTC) foo.rs --extern bar=foo.rs && exit 1 || exit 0
-	$(RUSTC) foo.rs \
-		--extern bar=$(TMPDIR)/libbar.rlib \
-		--extern bar=$(TMPDIR)/libbar-alt.rlib \
-		&& exit 1 || exit 0
-	$(RUSTC) foo.rs \
-		--extern bar=$(TMPDIR)/libbar.rlib \
-		--extern bar=$(TMPDIR)/libbar-a.rlib
-	$(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib
-	# Try to be sneaky and load a private crate from with a non-private name.
-	$(RUSTC) rustc.rs -Zforce-unstable-if-unmarked --crate-type=rlib
-	$(RUSTC) gated_unstable.rs --extern alloc=$(TMPDIR)/librustc.rlib 2>&1 | $(CGREP) 'rustc_private'
diff --git a/tests/run-make/extern-flag-fun/rmake.rs b/tests/run-make/extern-flag-fun/rmake.rs
new file mode 100644
index 00000000000..c1825f6bbb8
--- /dev/null
+++ b/tests/run-make/extern-flag-fun/rmake.rs
@@ -0,0 +1,38 @@
+// The --extern flag can override the default crate search of
+// the compiler and directly fetch a given path. There are a few rules
+// to follow: for example, there can't be more than one rlib, the crates must
+// be valid ("no-exist" in this test), and private crates can't be loaded
+// as non-private. This test checks that these rules are enforced.
+// See https://github.com/rust-lang/rust/pull/15319
+
+use run_make_support::{rust_lib_name, rustc};
+
+fn main() {
+    rustc().input("bar.rs").crate_type("rlib").run();
+    // Exactly the same rlib as the first line, only the filename changes.
+    rustc().input("bar.rs").crate_type("rlib").extra_filename("-a").run();
+    rustc().input("bar-alt.rs").crate_type("rlib").run();
+    // The crate must be valid.
+    rustc().input("foo.rs").extern_("bar", "no-exist").run_fail();
+    rustc().input("foo.rs").extern_("bar", "foo.rs").run_fail();
+    // Compilation fails with two different rlibs.
+    rustc()
+        .input("foo.rs")
+        .extern_("bar", rust_lib_name("bar"))
+        .extern_("bar", rust_lib_name("bar-alt"))
+        .run_fail();
+    // Even though this one has seemingly two rlibs, they are one and the same.
+    rustc()
+        .input("foo.rs")
+        .extern_("bar", rust_lib_name("bar"))
+        .extern_("bar", rust_lib_name("bar-a"))
+        .run();
+    rustc().input("foo.rs").extern_("bar", rust_lib_name("bar")).run();
+    // Try to be sneaky and load a private crate from with a non-private name.
+    rustc().input("rustc.rs").arg("-Zforce-unstable-if-unmarked").crate_type("rlib").run();
+    rustc()
+        .input("gated_unstable.rs")
+        .extern_("alloc", rust_lib_name("rustc"))
+        .run_fail()
+        .assert_stderr_contains("rustc_private");
+}
diff --git a/tests/run-make/incremental-debugger-visualizer/Makefile b/tests/run-make/incremental-debugger-visualizer/Makefile
deleted file mode 100644
index 8cfe41597ad..00000000000
--- a/tests/run-make/incremental-debugger-visualizer/Makefile
+++ /dev/null
@@ -1,49 +0,0 @@
-include ../tools.mk
-
-# This test makes sure that changes to files referenced via #[debugger_visualizer]
-# are picked up when compiling incrementally.
-
-# We have to copy the source to $(TMPDIR) because Github CI mounts the source
-# directory as readonly. We need to apply modifications to some of the source
-# file.
-SRC_DIR := $(TMPDIR)/src
-INCR_CACHE_DIR := $(TMPDIR)/incremental
-
-all:
-	rm -rf $(TMPDIR)/*
-	mkdir $(SRC_DIR)
-	cp ./foo.rs $(SRC_DIR)
-	echo "GDB script v1" > $(SRC_DIR)/foo.py
-	echo "Natvis v1" > $(SRC_DIR)/foo.natvis
-	$(RUSTC) $(SRC_DIR)/foo.rs \
-	  --crate-type=rlib \
-	  --emit metadata \
-	  -C incremental=$(INCR_CACHE_DIR) \
-	  -Z incremental-verify-ich
-	$(CGREP) "GDB script v1" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta
-
-	# Change only the GDB script and check that the change has been picked up
-	echo "GDB script v2" > $(SRC_DIR)/foo.py
-	$(RUSTC) $(SRC_DIR)/foo.rs \
-	  --crate-type=rlib \
-	  --emit metadata \
-	  -C incremental=$(INCR_CACHE_DIR) \
-	  -Z incremental-verify-ich
-
-	$(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) "Natvis v1" < $(TMPDIR)/libfoo.rmeta
-
-	# Now change the Natvis version and check that the change has been picked up
-	echo "Natvis v2" > $(SRC_DIR)/foo.natvis
-	$(RUSTC) $(SRC_DIR)/foo.rs \
-	  --crate-type=rlib \
-	  --emit metadata \
-	  -C incremental=$(INCR_CACHE_DIR) \
-	  -Z incremental-verify-ich
-
-	$(CGREP) "GDB script v2" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) -v "GDB script v1" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) "Natvis v2" < $(TMPDIR)/libfoo.rmeta
-	$(CGREP) -v "Natvis v1" < $(TMPDIR)/libfoo.rmeta
diff --git a/tests/run-make/incremental-debugger-visualizer/rmake.rs b/tests/run-make/incremental-debugger-visualizer/rmake.rs
new file mode 100644
index 00000000000..1ef3af87353
--- /dev/null
+++ b/tests/run-make/incremental-debugger-visualizer/rmake.rs
@@ -0,0 +1,56 @@
+// This test ensures that changes to files referenced via #[debugger_visualizer]
+// (in this case, foo.py and foo.natvis) are picked up when compiling incrementally.
+// See https://github.com/rust-lang/rust/pull/111641
+
+use run_make_support::{fs_wrapper, invalid_utf8_contains, invalid_utf8_not_contains, rustc};
+use std::io::Read;
+
+fn main() {
+    fs_wrapper::create_file("foo.py");
+    fs_wrapper::write("foo.py", "GDB script v1");
+    fs_wrapper::create_file("foo.natvis");
+    fs_wrapper::write("foo.natvis", "Natvis v1");
+    rustc()
+        .input("foo.rs")
+        .crate_type("rlib")
+        .emit("metadata")
+        .incremental("incremental")
+        .arg("-Zincremental-verify-ich")
+        .run();
+
+    invalid_utf8_contains("libfoo.rmeta", "GDB script v1");
+    invalid_utf8_contains("libfoo.rmeta", "Natvis v1");
+
+    // Change only the GDB script and check that the change has been picked up
+    fs_wrapper::remove_file("foo.py");
+    fs_wrapper::create_file("foo.py");
+    fs_wrapper::write("foo.py", "GDB script v2");
+    rustc()
+        .input("foo.rs")
+        .crate_type("rlib")
+        .emit("metadata")
+        .incremental("incremental")
+        .arg("-Zincremental-verify-ich")
+        .run();
+
+    invalid_utf8_contains("libfoo.rmeta", "GDB script v2");
+    invalid_utf8_not_contains("libfoo.rmeta", "GDB script v1");
+    invalid_utf8_contains("libfoo.rmeta", "Natvis v1");
+
+    // Now change the Natvis version and check that the change has been picked up
+    fs_wrapper::remove_file("foo.natvis");
+    fs_wrapper::create_file("foo.natvis");
+    fs_wrapper::write("foo.natvis", "Natvis v2");
+    rustc()
+        .input("foo.rs")
+        .crate_type("rlib")
+        .emit("metadata")
+        .incremental("incremental")
+        .arg("-Zincremental-verify-ich")
+        .run();
+
+    invalid_utf8_contains("libfoo.rmeta", "GDB script v2");
+    invalid_utf8_not_contains("libfoo.rmeta", "GDB script v1");
+    invalid_utf8_not_contains("libfoo.rmeta", "Natvis v1");
+    invalid_utf8_contains("libfoo.rmeta", "Natvis v2");
+}
diff --git a/tests/run-make/incremental-session-fail/Makefile b/tests/run-make/incremental-session-fail/Makefile
deleted file mode 100644
index f43eece2eb7..00000000000
--- a/tests/run-make/incremental-session-fail/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-include ../tools.mk
-
-SESSION_DIR := $(TMPDIR)/session
-OUTPUT_FILE := $(TMPDIR)/build-output
-
-all:
-	echo $(TMPDIR)
-	# Make it so that rustc will fail to create a session directory.
-	touch $(SESSION_DIR)
-	# Check exit code is 1 for an error, and not 101 for ICE.
-	$(RUSTC) foo.rs --crate-type=rlib -C incremental=$(SESSION_DIR) > $(OUTPUT_FILE) 2>&1; [ $$? -eq 1 ]
-	$(CGREP) "could not create incremental compilation crate directory" < $(OUTPUT_FILE)
-	# -v tests are fragile, hopefully this text won't change
-	$(CGREP) -v "internal compiler error" < $(OUTPUT_FILE)
diff --git a/tests/run-make/incremental-session-fail/rmake.rs b/tests/run-make/incremental-session-fail/rmake.rs
new file mode 100644
index 00000000000..0283709f2cf
--- /dev/null
+++ b/tests/run-make/incremental-session-fail/rmake.rs
@@ -0,0 +1,15 @@
+// Failing to create the directory where output incremental
+// files would be stored used to cause an ICE (Internal Compiler
+// Error). This was patched in #85698, and this test checks that
+// the ensuing compilation failure is not an ICE.
+// See https://github.com/rust-lang/rust/pull/85698
+
+use run_make_support::{fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::create_file("session");
+    // rustc should fail to create the session directory here.
+    let out = rustc().input("foo.rs").crate_type("rlib").incremental("session").run_fail();
+    out.assert_stderr_contains("could not create incremental compilation crate directory");
+    out.assert_stderr_not_contains("internal compiler error");
+}
diff --git a/tests/run-make/invalid-staticlib/Makefile b/tests/run-make/invalid-staticlib/Makefile
deleted file mode 100644
index 3f0f74ce3cb..00000000000
--- a/tests/run-make/invalid-staticlib/Makefile
+++ /dev/null
@@ -1,5 +0,0 @@
-include ../tools.mk
-
-all:
-	touch $(TMPDIR)/libfoo.a
-	echo | $(RUSTC) - --crate-type=rlib -lstatic=foo 2>&1 | $(CGREP) "failed to add native library"
diff --git a/tests/run-make/invalid-staticlib/rmake.rs b/tests/run-make/invalid-staticlib/rmake.rs
new file mode 100644
index 00000000000..45129293247
--- /dev/null
+++ b/tests/run-make/invalid-staticlib/rmake.rs
@@ -0,0 +1,17 @@
+// If the static library provided is not valid (in this test,
+// created as an empty file),
+// rustc should print a normal error message and not throw
+// an internal compiler error (ICE).
+// See https://github.com/rust-lang/rust/pull/28673
+
+use run_make_support::{fs_wrapper, rustc, static_lib_name};
+
+fn main() {
+    fs_wrapper::create_file(static_lib_name("foo"));
+    rustc()
+        .arg("-")
+        .crate_type("rlib")
+        .arg("-lstatic=foo")
+        .run_fail()
+        .assert_stderr_contains("failed to add native library");
+}
diff --git a/tests/run-make/issue-64153/Makefile b/tests/run-make/issue-64153/Makefile
deleted file mode 100644
index f42ea620fb9..00000000000
--- a/tests/run-make/issue-64153/Makefile
+++ /dev/null
@@ -1,26 +0,0 @@
-include ../tools.mk
-
-# `llvm-objdump`'s output looks different on windows than on other platforms.
-# It should be enough to check on Unix platforms, so:
-# ignore-windows
-
-# Staticlibs don't include Rust object files from upstream crates if the same
-# code was already pulled into the lib via LTO. However, the bug described in
-# https://github.com/rust-lang/rust/issues/64153 lead to this exclusion not
-# working properly if the upstream crate was compiled with an explicit filename
-# (via `-o`).
-#
-# This test makes sure that functions defined in the upstream crates do not
-# appear twice in the final staticlib when listing all the symbols from it.
-
-all:
-	$(RUSTC) --crate-type rlib upstream.rs -o $(TMPDIR)/libupstream.rlib -Ccodegen-units=1
-	$(RUSTC) --crate-type staticlib downstream.rs -Clto -Ccodegen-units=1 -o $(TMPDIR)/libdownstream.a
-	# Dump all the symbols from the staticlib into `syms`
-	"$(LLVM_BIN_DIR)"/llvm-objdump -t $(TMPDIR)/libdownstream.a > $(TMPDIR)/syms
-	# Count the global instances of `issue64153_test_function`. There'll be 2
-	# if the `upstream` object file got erroneously included twice.
-	# The line we are testing for with the regex looks something like:
-	# 0000000000000000 g     F .text.issue64153_test_function	00000023 issue64153_test_function
-	grep -c -e "[[:space:]]g[[:space:]]*F[[:space:]].*issue64153_test_function" $(TMPDIR)/syms > $(TMPDIR)/count
-	[ "$$(cat $(TMPDIR)/count)" -eq "1" ]
diff --git a/tests/run-make/issue-64153/downstream.rs b/tests/run-make/lto-avoid-object-duplication/downstream.rs
index e03704665d4..e03704665d4 100644
--- a/tests/run-make/issue-64153/downstream.rs
+++ b/tests/run-make/lto-avoid-object-duplication/downstream.rs
diff --git a/tests/run-make/lto-avoid-object-duplication/rmake.rs b/tests/run-make/lto-avoid-object-duplication/rmake.rs
new file mode 100644
index 00000000000..b0e7494cb51
--- /dev/null
+++ b/tests/run-make/lto-avoid-object-duplication/rmake.rs
@@ -0,0 +1,40 @@
+// ignore-tidy-tab
+// Staticlibs don't include Rust object files from upstream crates if the same
+// code was already pulled into the lib via LTO. However, the bug described in
+// https://github.com/rust-lang/rust/issues/64153 lead to this exclusion not
+// working properly if the upstream crate was compiled with an explicit filename
+// (via `-o`).
+
+// This test makes sure that functions defined in the upstream crates do not
+// appear twice in the final staticlib when listing all the symbols from it.
+
+//@ ignore-windows
+// Reason: `llvm-objdump`'s output looks different on windows than on other platforms.
+// Only checking on Unix platforms should suffice.
+//FIXME(Oneirical): This could be adapted to work on Windows by checking how
+// that output differs.
+
+use run_make_support::{llvm_objdump, regex, rust_lib_name, rustc, static_lib_name};
+
+fn main() {
+    rustc()
+        .crate_type("rlib")
+        .input("upstream.rs")
+        .output(rust_lib_name("upstream"))
+        .codegen_units(1)
+        .run();
+    rustc()
+        .crate_type("staticlib")
+        .input("downstream.rs")
+        .arg("-Clto")
+        .output(static_lib_name("downstream"))
+        .codegen_units(1)
+        .run();
+    let syms = llvm_objdump().arg("-t").input(static_lib_name("downstream")).run().stdout_utf8();
+    let re = regex::Regex::new(r#"\s*g\s*F\s.*issue64153_test_function"#).unwrap();
+    // Count the global instances of `issue64153_test_function`. There'll be 2
+    // if the `upstream` object file got erroneously included twice.
+    // The line we are testing for with the regex looks something like:
+    // 0000000000000000 g     F .text.issue64153_test_function	00000023 issue64153_test_function
+    assert_eq!(re.find_iter(syms.as_str()).count(), 1);
+}
diff --git a/tests/run-make/issue-64153/upstream.rs b/tests/run-make/lto-avoid-object-duplication/upstream.rs
index 861a00298ea..861a00298ea 100644
--- a/tests/run-make/issue-64153/upstream.rs
+++ b/tests/run-make/lto-avoid-object-duplication/upstream.rs
diff --git a/tests/run-make/native-link-modifier-verbatim-linker/Makefile b/tests/run-make/native-link-modifier-verbatim-linker/Makefile
deleted file mode 100644
index 47ed2a14291..00000000000
--- a/tests/run-make/native-link-modifier-verbatim-linker/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# ignore-cross-compile
-# ignore-apple
-
-include ../tools.mk
-
-all:
-	# Verbatim allows specify precise name.
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_some_strange_name.ext
-	$(RUSTC) main.rs -l static:+verbatim=local_some_strange_name.ext
-
-	# With verbatim any other name cannot be used (local).
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/liblocal_native_dep.a
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.a
-	$(RUSTC) local_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/local_native_dep.lib
-	$(RUSTC) main.rs -l static:+verbatim=local_native_dep 2>&1 | $(CGREP) "local_native_dep"
diff --git a/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
new file mode 100644
index 00000000000..6868cb368cc
--- /dev/null
+++ b/tests/run-make/native-link-modifier-verbatim-linker/rmake.rs
@@ -0,0 +1,41 @@
+// `verbatim` is a native link modifier that forces rustc to only accept libraries with
+// a specified name. This test checks that this modifier works as intended.
+// This test is the same as native-link-modifier-rustc, but without rlibs.
+// See https://github.com/rust-lang/rust/issues/99425
+
+//@ ignore-apple
+// Reason: linking fails due to the unusual ".ext" staticlib name.
+
+use run_make_support::rustc;
+
+fn main() {
+    // Verbatim allows for the specification of a precise name
+    // - in this case, the unconventional ".ext" extension.
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("local_some_strange_name.ext")
+        .run();
+    rustc().input("main.rs").arg("-lstatic:+verbatim=local_some_strange_name.ext").run();
+
+    // This section voluntarily avoids using static_lib_name helpers to be verbatim.
+    // With verbatim, even these common library names are refused
+    // - it wants local_native_dep without
+    // any file extensions.
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("liblocal_native_dep.a")
+        .run();
+    rustc().input("local_native_dep.rs").crate_type("staticlib").output("local_native_dep.a").run();
+    rustc()
+        .input("local_native_dep.rs")
+        .crate_type("staticlib")
+        .output("local_native_dep.lib")
+        .run();
+    rustc()
+        .input("main.rs")
+        .arg("-lstatic:+verbatim=local_native_dep")
+        .run_fail()
+        .assert_stderr_contains("local_native_dep");
+}
diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile b/tests/run-make/native-link-modifier-verbatim-rustc/Makefile
deleted file mode 100644
index dfd6ec50fc0..00000000000
--- a/tests/run-make/native-link-modifier-verbatim-rustc/Makefile
+++ /dev/null
@@ -1,12 +0,0 @@
-include ../tools.mk
-
-all:
-	# Verbatim allows specify precise name.
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_some_strange_name.ext
-	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_some_strange_name.ext --crate-type rlib
-
-	# With verbatim any other name cannot be used (upstream).
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/libupstream_native_dep.a
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.a
-	$(RUSTC) upstream_native_dep.rs --crate-type=staticlib -o $(TMPDIR)/upstream_native_dep.lib
-	$(RUSTC) rust_dep.rs -l static:+verbatim=upstream_native_dep --crate-type rlib 2>&1 | $(CGREP) "upstream_native_dep"
diff --git a/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs
new file mode 100644
index 00000000000..703b8a80ef3
--- /dev/null
+++ b/tests/run-make/native-link-modifier-verbatim-rustc/rmake.rs
@@ -0,0 +1,47 @@
+// `verbatim` is a native link modifier that forces rustc to only accept libraries with
+// a specified name. This test checks that this modifier works as intended.
+// This test is the same as native-link-modifier-linker, but with rlibs.
+// See https://github.com/rust-lang/rust/issues/99425
+
+use run_make_support::rustc;
+
+fn main() {
+    // Verbatim allows for the specification of a precise name
+    // - in this case, the unconventional ".ext" extension.
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_some_strange_name.ext")
+        .run();
+    rustc()
+        .input("rust_dep.rs")
+        .crate_type("rlib")
+        .arg("-lstatic:+verbatim=upstream_some_strange_name.ext")
+        .run();
+
+    // This section voluntarily avoids using static_lib_name helpers to be verbatim.
+    // With verbatim, even these common library names are refused
+    // - it wants upstream_native_dep without
+    // any file extensions.
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("libupstream_native_dep.a")
+        .run();
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_native_dep.a")
+        .run();
+    rustc()
+        .input("upstream_native_dep.rs")
+        .crate_type("staticlib")
+        .output("upstream_native_dep.lib")
+        .run();
+    rustc()
+        .input("rust_dep.rs")
+        .crate_type("rlib")
+        .arg("-lstatic:+verbatim=upstream_native_dep")
+        .run_fail()
+        .assert_stderr_contains("upstream_native_dep");
+}
diff --git a/tests/run-make/no-builtins-lto/Makefile b/tests/run-make/no-builtins-lto/Makefile
deleted file mode 100644
index c8f05d9918b..00000000000
--- a/tests/run-make/no-builtins-lto/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-include ../tools.mk
-
-all:
-	# Compile a `#![no_builtins]` rlib crate
-	$(RUSTC) no_builtins.rs
-	# Build an executable that depends on that crate using LTO. The no_builtins crate doesn't
-	# participate in LTO, so its rlib must be explicitly linked into the final binary. Verify this by
-	# grepping the linker arguments.
-	$(RUSTC) main.rs -C lto --print link-args | $(CGREP) 'libno_builtins.rlib'
diff --git a/tests/run-make/no-builtins-lto/rmake.rs b/tests/run-make/no-builtins-lto/rmake.rs
new file mode 100644
index 00000000000..8e0c3a63649
--- /dev/null
+++ b/tests/run-make/no-builtins-lto/rmake.rs
@@ -0,0 +1,20 @@
+// The rlib produced by a no_builtins crate should be explicitely linked
+// during compilation, and as a result be present in the linker arguments.
+// See the comments inside this file for more details.
+// See https://github.com/rust-lang/rust/pull/35637
+
+use run_make_support::{rust_lib_name, rustc};
+
+fn main() {
+    // Compile a `#![no_builtins]` rlib crate
+    rustc().input("no_builtins.rs").run();
+    // Build an executable that depends on that crate using LTO. The no_builtins crate doesn't
+    // participate in LTO, so its rlib must be explicitly
+    // linked into the final binary. Verify this by grepping the linker arguments.
+    rustc()
+        .input("main.rs")
+        .arg("-Clto")
+        .print("link-args")
+        .run()
+        .assert_stdout_contains(rust_lib_name("no_builtins"));
+}
diff --git a/tests/run-make/output-filename-conflicts-with-directory/Makefile b/tests/run-make/output-filename-conflicts-with-directory/Makefile
deleted file mode 100644
index 45221356cd9..00000000000
--- a/tests/run-make/output-filename-conflicts-with-directory/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-all:
-	cp foo.rs $(TMPDIR)/foo.rs
-	mkdir $(TMPDIR)/foo
-	$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo 2>&1 \
-		| $(CGREP) -e "the generated executable for the input file \".*foo\.rs\" conflicts with the existing directory \".*foo\""
diff --git a/tests/run-make/output-filename-conflicts-with-directory/rmake.rs b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs
new file mode 100644
index 00000000000..4b5c9e8d118
--- /dev/null
+++ b/tests/run-make/output-filename-conflicts-with-directory/rmake.rs
@@ -0,0 +1,14 @@
+// ignore-tidy-linelength
+// When the compiled executable would conflict with a directory, a
+// rustc error should be displayed instead of a verbose and
+// potentially-confusing linker error.
+// See https://github.com/rust-lang/rust/pull/47203
+
+use run_make_support::{fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::create_dir("foo");
+    rustc().input("foo.rs").output("foo").run_fail().assert_stderr_contains(
+        r#"the generated executable for the input file "foo.rs" conflicts with the existing directory "foo""#,
+    );
+}
diff --git a/tests/run-make/output-filename-overwrites-input/Makefile b/tests/run-make/output-filename-overwrites-input/Makefile
deleted file mode 100644
index fe5d231382d..00000000000
--- a/tests/run-make/output-filename-overwrites-input/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	cp foo.rs $(TMPDIR)/foo
-	$(RUSTC) $(TMPDIR)/foo -o $(TMPDIR)/foo 2>&1 \
-		| $(CGREP) -e "the input file \".*foo\" would be overwritten by the generated executable"
-	cp bar.rs $(TMPDIR)/bar.rlib
-	$(RUSTC) $(TMPDIR)/bar.rlib -o $(TMPDIR)/bar.rlib 2>&1 \
-		| $(CGREP) -e "the input file \".*bar.rlib\" would be overwritten by the generated executable"
-	$(RUSTC) foo.rs 2>&1 && $(RUSTC) -Z ls=root $(TMPDIR)/foo 2>&1
-	cp foo.rs $(TMPDIR)/foo.rs
-	$(RUSTC) $(TMPDIR)/foo.rs -o $(TMPDIR)/foo.rs 2>&1 \
-		| $(CGREP) -e "the input file \".*foo.rs\" would be overwritten by the generated executable"
diff --git a/tests/run-make/output-filename-overwrites-input/rmake.rs b/tests/run-make/output-filename-overwrites-input/rmake.rs
new file mode 100644
index 00000000000..c6055e818a1
--- /dev/null
+++ b/tests/run-make/output-filename-overwrites-input/rmake.rs
@@ -0,0 +1,21 @@
+// If rustc is invoked on a file that would be overwritten by the
+// compilation, the compilation should fail, to avoid accidental loss.
+// See https://github.com/rust-lang/rust/pull/46814
+
+//@ ignore-cross-compile
+
+use run_make_support::{fs_wrapper, rustc};
+
+fn main() {
+    fs_wrapper::copy("foo.rs", "foo");
+    rustc().input("foo").output("foo").run_fail().assert_stderr_contains(
+        r#"the input file "foo" would be overwritten by the generated executable"#,
+    );
+    fs_wrapper::copy("bar.rs", "bar.rlib");
+    rustc().input("bar.rlib").output("bar.rlib").run_fail().assert_stderr_contains(
+        r#"the input file "bar.rlib" would be overwritten by the generated executable"#,
+    );
+    rustc().input("foo.rs").output("foo.rs").run_fail().assert_stderr_contains(
+        r#"the input file "foo.rs" would be overwritten by the generated executable"#,
+    );
+}
diff --git a/tests/run-make/separate-link-fail/Makefile b/tests/run-make/separate-link-fail/Makefile
deleted file mode 100644
index bfd18fbf972..00000000000
--- a/tests/run-make/separate-link-fail/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-all:
-	echo 'fn main(){}' > $(TMPDIR)/main.rs
-	# Make sure that this fails
-	! $(RUSTC) -Z link-only $(TMPDIR)/main.rs 2> $(TMPDIR)/stderr.txt
-	$(CGREP) "The input does not look like a .rlink file" < $(TMPDIR)/stderr.txt
diff --git a/tests/run-make/separate-link-fail/foo.rs b/tests/run-make/separate-link-fail/foo.rs
new file mode 100644
index 00000000000..f328e4d9d04
--- /dev/null
+++ b/tests/run-make/separate-link-fail/foo.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/tests/run-make/separate-link-fail/rmake.rs b/tests/run-make/separate-link-fail/rmake.rs
new file mode 100644
index 00000000000..b5d5300de68
--- /dev/null
+++ b/tests/run-make/separate-link-fail/rmake.rs
@@ -0,0 +1,15 @@
+// rustc usually wants Rust code as its input. The flag `link-only` is one
+// exception, where a .rlink file is instead requested. The compiler should
+// fail when the user is wrongly passing the original Rust code
+// instead of the generated .rlink file when this flag is on.
+// https://github.com/rust-lang/rust/issues/95297
+
+use run_make_support::rustc;
+
+fn main() {
+    rustc()
+        .arg("-Zlink-only")
+        .input("foo.rs")
+        .run_fail()
+        .assert_stderr_contains("The input does not look like a .rlink file");
+}
diff --git a/tests/run-make/separate-link/Makefile b/tests/run-make/separate-link/Makefile
deleted file mode 100644
index d01158d9f5f..00000000000
--- a/tests/run-make/separate-link/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-# ignore-cross-compile
-include ../tools.mk
-
-all:
-	echo 'fn main(){}' | $(RUSTC) -Z no-link -
-	$(RUSTC) -Z link-only $(TMPDIR)/rust_out.rlink
-	$(call RUN,rust_out)
diff --git a/tests/run-make/separate-link/rmake.rs b/tests/run-make/separate-link/rmake.rs
new file mode 100644
index 00000000000..e91b25489bc
--- /dev/null
+++ b/tests/run-make/separate-link/rmake.rs
@@ -0,0 +1,14 @@
+// The compiler flags no-link (and by extension, link-only) used to be broken
+// due to changes in encoding/decoding. This was patched, and this test checks
+// that these flags are not broken again, resulting in successful compilation.
+// See https://github.com/rust-lang/rust/issues/77857
+
+//@ ignore-cross-compile
+
+use run_make_support::{run, rustc};
+
+fn main() {
+    rustc().stdin(b"fn main(){}").arg("-Zno-link").arg("-").run();
+    rustc().arg("-Zlink-only").input("rust_out.rlink").run();
+    run("rust_out");
+}
diff --git a/tests/run-make/used/Makefile b/tests/run-make/used/Makefile
deleted file mode 100644
index e80eb9e4020..00000000000
--- a/tests/run-make/used/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-include ../tools.mk
-
-# ignore-windows-msvc
-
-all:
-	$(RUSTC) -C opt-level=3 --emit=obj used.rs
-	nm $(TMPDIR)/used.o | $(CGREP) FOO
diff --git a/tests/run-make/used/rmake.rs b/tests/run-make/used/rmake.rs
new file mode 100644
index 00000000000..56ef5c6b9cc
--- /dev/null
+++ b/tests/run-make/used/rmake.rs
@@ -0,0 +1,15 @@
+// This test ensures that the compiler is keeping static variables, even if not referenced
+// by another part of the program, in the output object file.
+//
+// It comes from #39987 which implements this RFC for the #[used] attribute:
+// https://rust-lang.github.io/rfcs/2386-used.html
+
+//@ ignore-msvc
+
+use run_make_support::{cmd, rustc};
+
+fn main() {
+    rustc().opt_level("3").emit("obj").input("used.rs").run();
+
+    cmd("nm").arg("used.o").run().assert_stdout_contains("FOO");
+}
diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs
index 7c3a2c97474..5fcff74064a 100644
--- a/tests/ui-fulldeps/internal-lints/diagnostics.rs
+++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs
@@ -14,8 +14,8 @@ extern crate rustc_session;
 extern crate rustc_span;
 
 use rustc_errors::{
-    Diag, DiagCtxt, DiagInner, DiagMessage, Diagnostic, EmissionGuarantee, Level, LintDiagnostic,
-    SubdiagMessageOp, SubdiagMessage, Subdiagnostic,
+    Diag, DiagCtxtHandle, DiagInner, DiagMessage, Diagnostic, EmissionGuarantee, Level,
+    LintDiagnostic, SubdiagMessage, SubdiagMessageOp, Subdiagnostic,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::Span;
@@ -39,7 +39,7 @@ struct Note {
 pub struct UntranslatableInDiagnostic;
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UntranslatableInDiagnostic {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         Diag::new(dcx, level, "untranslatable diagnostic")
         //~^ ERROR diagnostics should be created using translatable messages
     }
@@ -48,7 +48,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for UntranslatableInDiagnostic
 pub struct TranslatableInDiagnostic;
 
 impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for TranslatableInDiagnostic {
-    fn into_diag(self, dcx: &'a DiagCtxt, level: Level) -> Diag<'a, G> {
+    fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> {
         Diag::new(dcx, level, crate::fluent_generated::no_crate_example)
     }
 }
@@ -81,7 +81,7 @@ impl Subdiagnostic for TranslatableInAddtoDiag {
 pub struct UntranslatableInLintDiagnostic;
 
 impl<'a> LintDiagnostic<'a, ()> for UntranslatableInLintDiagnostic {
-    fn decorate_lint<'b, >(self, diag: &'b mut Diag<'a, ()>) {
+    fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) {
         diag.note("untranslatable diagnostic");
         //~^ ERROR diagnostics should be created using translatable messages
     }
@@ -95,7 +95,7 @@ impl<'a> LintDiagnostic<'a, ()> for TranslatableInLintDiagnostic {
     }
 }
 
-pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) {
+pub fn make_diagnostics<'a>(dcx: DiagCtxtHandle<'a>) {
     let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example);
     //~^ ERROR diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
 
@@ -107,7 +107,7 @@ pub fn make_diagnostics<'a>(dcx: &'a DiagCtxt) {
 // Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted for
 // `diagnostic_outside_of_impl`.
 #[rustc_lint_diagnostics]
-pub fn skipped_because_of_annotation<'a>(dcx: &'a DiagCtxt) {
+pub fn skipped_because_of_annotation<'a>(dcx: DiagCtxtHandle<'a>) {
     #[allow(rustc::untranslatable_diagnostic)]
     let _diag = dcx.struct_err("untranslatable diagnostic"); // okay!
 }
diff --git a/tests/ui/array-slice-vec/vec-res-add.stderr b/tests/ui/array-slice-vec/vec-res-add.stderr
index cf5796f7e4a..34fd69426a8 100644
--- a/tests/ui/array-slice-vec/vec-res-add.stderr
+++ b/tests/ui/array-slice-vec/vec-res-add.stderr
@@ -5,6 +5,11 @@ LL |     let k = i + j;
    |             - ^ - Vec<R>
    |             |
    |             Vec<R>
+   |
+note: the foreign item type `Vec<R>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/autoderef-full-lval.stderr b/tests/ui/autoderef-full-lval.stderr
index 9921ce7c154..d90238a7fb2 100644
--- a/tests/ui/autoderef-full-lval.stderr
+++ b/tests/ui/autoderef-full-lval.stderr
@@ -5,6 +5,12 @@ LL |     let z: isize = a.x + b.y;
    |                    --- ^ --- Box<isize>
    |                    |
    |                    Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error[E0369]: cannot add `Box<isize>` to `Box<isize>`
   --> $DIR/autoderef-full-lval.rs:21:33
@@ -13,6 +19,12 @@ LL |     let answer: isize = forty.a + two.a;
    |                         ------- ^ ----- Box<isize>
    |                         |
    |                         Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add`
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.rs b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs
new file mode 100644
index 00000000000..8827bbb003b
--- /dev/null
+++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.rs
@@ -0,0 +1,16 @@
+use std::io::{Error, ErrorKind};
+use std::thread;
+
+struct T1;
+struct T2;
+
+fn main() {
+    (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
+    //~^ERROR binary operation `==` cannot be applied to type
+    (Error::new(ErrorKind::Other, "2"), thread::current())
+        == (Error::new(ErrorKind::Other, "2"), thread::current());
+    //~^ERROR binary operation `==` cannot be applied to type
+    (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
+        == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
+    //~^ERROR binary operation `==` cannot be applied to type
+}
diff --git a/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr
new file mode 100644
index 00000000000..1cf75bbc1a5
--- /dev/null
+++ b/tests/ui/binop/binary-op-not-allowed-issue-125631.stderr
@@ -0,0 +1,75 @@
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, T1, {integer})`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:8:48
+   |
+LL |     (Error::new(ErrorKind::Other, "1"), T1, 1) == (Error::new(ErrorKind::Other, "1"), T1, 2);
+   |     ------------------------------------------ ^^ ------------------------------------------ (std::io::Error, T1, {integer})
+   |     |
+   |     (std::io::Error, T1, {integer})
+   |
+note: an implementation of `PartialEq` might be missing for `T1`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
+   |
+LL | struct T1;
+   | ^^^^^^^^^ must implement `PartialEq`
+note: the foreign item type `std::io::Error` doesn't implement `PartialEq`
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+help: consider annotating `T1` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T1;
+   |
+
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread)`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:11:9
+   |
+LL |     (Error::new(ErrorKind::Other, "2"), thread::current())
+   |     ------------------------------------------------------ (std::io::Error, Thread)
+LL |         == (Error::new(ErrorKind::Other, "2"), thread::current());
+   |         ^^ ------------------------------------------------------ (std::io::Error, Thread)
+   |
+note: the foreign item types don't implement required traits for this operation to be valid
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+
+error[E0369]: binary operation `==` cannot be applied to type `(std::io::Error, Thread, T1, T2)`
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:14:9
+   |
+LL |     (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2)
+   |     -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
+LL |         == (Error::new(ErrorKind::Other, "4"), thread::current(), T1, T2);
+   |         ^^ -------------------------------------------------------------- (std::io::Error, Thread, T1, T2)
+   |
+note: the following types would have to `impl` their required traits for this operation to be valid
+  --> $DIR/binary-op-not-allowed-issue-125631.rs:4:1
+   |
+LL | struct T1;
+   | ^^^^^^^^^ must implement `PartialEq`
+LL | struct T2;
+   | ^^^^^^^^^ must implement `PartialEq`
+note: the foreign item types don't implement required traits for this operation to be valid
+  --> $SRC_DIR/std/src/io/error.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+  --> $SRC_DIR/std/src/thread/mod.rs:LL:COL
+   |
+   = note: not implement `PartialEq`
+help: consider annotating `T1` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T1;
+   |
+help: consider annotating `T2` with `#[derive(PartialEq)]`
+   |
+LL + #[derive(PartialEq)]
+LL | struct T2;
+   |
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0369`.
diff --git a/tests/ui/binop/binop-bitxor-str.stderr b/tests/ui/binop/binop-bitxor-str.stderr
index 20b1ecc5a93..9d9ec6c5af6 100644
--- a/tests/ui/binop/binop-bitxor-str.stderr
+++ b/tests/ui/binop/binop-bitxor-str.stderr
@@ -5,6 +5,11 @@ LL | fn main() { let x = "a".to_string() ^ "b".to_string(); }
    |                     --------------- ^ --------------- String
    |                     |
    |                     String
+   |
+note: the foreign item type `String` doesn't implement `BitXor`
+  --> $SRC_DIR/alloc/src/string.rs:LL:COL
+   |
+   = note: not implement `BitXor`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/coverage-attr/bad-syntax.rs b/tests/ui/coverage-attr/bad-syntax.rs
new file mode 100644
index 00000000000..8783714992b
--- /dev/null
+++ b/tests/ui/coverage-attr/bad-syntax.rs
@@ -0,0 +1,58 @@
+#![feature(coverage_attribute)]
+
+// Tests the error messages produced (or not produced) by various unusual
+// uses of the `#[coverage(..)]` attribute.
+
+// FIXME(#84605): Multiple coverage attributes with the same value are useless,
+// and should probably produce a diagnostic.
+#[coverage(off)]
+#[coverage(off)]
+fn multiple_consistent() {}
+
+// FIXME(#84605): When there are multiple inconsistent coverage attributes,
+// it's unclear which one will prevail.
+#[coverage(off)]
+#[coverage(on)]
+fn multiple_inconsistent() {}
+
+#[coverage] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bare_word() {}
+
+// FIXME(#84605): This shows as multiple different errors, one of which suggests
+// writing bare `#[coverage]`, which is not allowed.
+#[coverage = true]
+//~^ ERROR expected `coverage(off)` or `coverage(on)`
+//~| ERROR malformed `coverage` attribute input
+//~| HELP the following are the possible correct uses
+//~| SUGGESTION #[coverage(on|off)]
+fn key_value() {}
+
+#[coverage()] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_empty() {}
+
+#[coverage(off, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_consistent() {}
+
+#[coverage(off, on)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn list_inconsistent() {}
+
+#[coverage(bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word() {}
+
+#[coverage(bogus, off)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word_before() {}
+
+#[coverage(off, bogus)] //~ ERROR expected `coverage(off)` or `coverage(on)`
+fn bogus_word_after() {}
+
+#[coverage(off,)]
+fn comma_after() {}
+
+// FIXME(#84605): This shows as multiple different errors.
+#[coverage(,off)]
+//~^ ERROR expected identifier, found `,`
+//~| HELP remove this comma
+//~| ERROR expected `coverage(off)` or `coverage(on)`
+fn comma_before() {}
+
+fn main() {}
diff --git a/tests/ui/coverage-attr/bad-syntax.stderr b/tests/ui/coverage-attr/bad-syntax.stderr
new file mode 100644
index 00000000000..f6181d12a94
--- /dev/null
+++ b/tests/ui/coverage-attr/bad-syntax.stderr
@@ -0,0 +1,78 @@
+error: malformed `coverage` attribute input
+  --> $DIR/bad-syntax.rs:23:1
+   |
+LL | #[coverage = true]
+   | ^^^^^^^^^^^^^^^^^^
+   |
+help: the following are the possible correct uses
+   |
+LL | #[coverage(on|off)]
+   |
+LL | #[coverage]
+   |
+
+error: expected identifier, found `,`
+  --> $DIR/bad-syntax.rs:52:12
+   |
+LL | #[coverage(,off)]
+   |            ^
+   |            |
+   |            expected identifier
+   |            help: remove this comma
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:18:1
+   |
+LL | #[coverage]
+   | ^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:23:1
+   |
+LL | #[coverage = true]
+   | ^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:30:1
+   |
+LL | #[coverage()]
+   | ^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:33:1
+   |
+LL | #[coverage(off, off)]
+   | ^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:36:1
+   |
+LL | #[coverage(off, on)]
+   | ^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:39:1
+   |
+LL | #[coverage(bogus)]
+   | ^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:42:1
+   |
+LL | #[coverage(bogus, off)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:45:1
+   |
+LL | #[coverage(off, bogus)]
+   | ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: expected `coverage(off)` or `coverage(on)`
+  --> $DIR/bad-syntax.rs:52:1
+   |
+LL | #[coverage(,off)]
+   | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 11 previous errors
+
diff --git a/tests/ui/lint/no-coverage.rs b/tests/ui/coverage-attr/no-coverage.rs
index 907d25d333e..907d25d333e 100644
--- a/tests/ui/lint/no-coverage.rs
+++ b/tests/ui/coverage-attr/no-coverage.rs
diff --git a/tests/ui/lint/no-coverage.stderr b/tests/ui/coverage-attr/no-coverage.stderr
index a87b0fb49f0..a87b0fb49f0 100644
--- a/tests/ui/lint/no-coverage.stderr
+++ b/tests/ui/coverage-attr/no-coverage.stderr
diff --git a/tests/ui/error-codes/E0067.stderr b/tests/ui/error-codes/E0067.stderr
index ec0358cb7df..71b72080544 100644
--- a/tests/ui/error-codes/E0067.stderr
+++ b/tests/ui/error-codes/E0067.stderr
@@ -5,6 +5,12 @@ LL |     LinkedList::new() += 1;
    |     -----------------^^^^^
    |     |
    |     cannot use `+=` on type `LinkedList<_>`
+   |
+note: the foreign item type `LinkedList<_>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/collections/linked_list.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 
 error[E0067]: invalid left-hand side of assignment
   --> $DIR/E0067.rs:4:23
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.rs b/tests/ui/feature-gates/feature-gate-precise-capturing.rs
index 0c3b4977623..47a21539d37 100644
--- a/tests/ui/feature-gates/feature-gate-precise-capturing.rs
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing.rs
@@ -1,4 +1,4 @@
-fn hello() -> impl use<> Sized {}
+fn hello() -> impl Sized + use<> {}
 //~^ ERROR precise captures on `impl Trait` are experimental
 
 fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr
index 102b39148f9..04365408880 100644
--- a/tests/ui/feature-gates/feature-gate-precise-capturing.stderr
+++ b/tests/ui/feature-gates/feature-gate-precise-capturing.stderr
@@ -1,8 +1,8 @@
 error[E0658]: precise captures on `impl Trait` are experimental
-  --> $DIR/feature-gate-precise-capturing.rs:1:20
+  --> $DIR/feature-gate-precise-capturing.rs:1:28
    |
-LL | fn hello() -> impl use<> Sized {}
-   |                    ^^^
+LL | fn hello() -> impl Sized + use<> {}
+   |                            ^^^
    |
    = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
    = help: add `#![feature(precise_capturing)]` to the crate attributes to enable
diff --git a/tests/ui/impl-trait/call_method_ambiguous.next.stderr b/tests/ui/impl-trait/call_method_ambiguous.next.stderr
index cd222aa7ae9..a1f9a8b40a8 100644
--- a/tests/ui/impl-trait/call_method_ambiguous.next.stderr
+++ b/tests/ui/impl-trait/call_method_ambiguous.next.stderr
@@ -1,5 +1,5 @@
 error[E0282]: type annotations needed
-  --> $DIR/call_method_ambiguous.rs:29:13
+  --> $DIR/call_method_ambiguous.rs:28:13
    |
 LL |         let mut iter = foo(n - 1, m);
    |             ^^^^^^^^
diff --git a/tests/ui/impl-trait/call_method_ambiguous.rs b/tests/ui/impl-trait/call_method_ambiguous.rs
index c26c01e002d..4dac605d6b8 100644
--- a/tests/ui/impl-trait/call_method_ambiguous.rs
+++ b/tests/ui/impl-trait/call_method_ambiguous.rs
@@ -3,7 +3,6 @@
 //@[current] run-pass
 
 #![feature(precise_capturing)]
-#![allow(incomplete_features)]
 
 trait Get {
     fn get(&mut self) -> u32;
@@ -24,7 +23,7 @@ where
     }
 }
 
-fn foo(n: usize, m: &mut ()) -> impl use<'_> Get {
+fn foo(n: usize, m: &mut ()) -> impl Get + use<'_> {
     if n > 0 {
         let mut iter = foo(n - 1, m);
         //[next]~^ type annotations needed
diff --git a/tests/ui/impl-trait/precise-capturing/apit.rs b/tests/ui/impl-trait/precise-capturing/apit.rs
index efcac9ebb0b..64c15d6df96 100644
--- a/tests/ui/impl-trait/precise-capturing/apit.rs
+++ b/tests/ui/impl-trait/precise-capturing/apit.rs
@@ -1,7 +1,6 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn hello(_: impl use<> Sized) {}
-//~^ ERROR `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
+fn hello(_: impl Sized + use<>) {}
+//~^ ERROR `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/apit.stderr b/tests/ui/impl-trait/precise-capturing/apit.stderr
index 96548f5732f..1d6225a1ff8 100644
--- a/tests/ui/impl-trait/precise-capturing/apit.stderr
+++ b/tests/ui/impl-trait/precise-capturing/apit.stderr
@@ -1,17 +1,8 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/apit.rs:1:12
+error: `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
+  --> $DIR/apit.rs:3:26
    |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-error: `use<...>` precise capturing syntax not allowed on argument-position `impl Trait`
-  --> $DIR/apit.rs:4:18
-   |
-LL | fn hello(_: impl use<> Sized) {}
-   |                  ^^^^^
+LL | fn hello(_: impl Sized + use<>) {}
+   |                          ^^^^^
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
index 623063a8f50..d2d4570c570 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
+++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.rs
@@ -1,14 +1,13 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn no_elided_lt() -> impl use<'_> Sized {}
+fn no_elided_lt() -> impl Sized + use<'_> {}
 //~^ ERROR missing lifetime specifier
 //~| ERROR expected lifetime parameter in `use<...>` precise captures list, found `'_`
 
-fn static_lt() -> impl use<'static> Sized {}
+fn static_lt() -> impl Sized + use<'static> {}
 //~^ ERROR expected lifetime parameter in `use<...>` precise captures list, found `'static`
 
-fn missing_lt() -> impl use<'missing> Sized {}
+fn missing_lt() -> impl Sized + use<'missing> {}
 //~^ ERROR use of undeclared lifetime name `'missing`
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
index a926362c50c..550996ab5e5 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
+++ b/tests/ui/impl-trait/precise-capturing/bad-lifetimes.stderr
@@ -1,45 +1,36 @@
 error[E0106]: missing lifetime specifier
-  --> $DIR/bad-lifetimes.rs:4:31
+  --> $DIR/bad-lifetimes.rs:3:39
    |
-LL | fn no_elided_lt() -> impl use<'_> Sized {}
-   |                               ^^ expected named lifetime parameter
+LL | fn no_elided_lt() -> impl Sized + use<'_> {}
+   |                                       ^^ expected named lifetime parameter
    |
    = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
 help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
    |
-LL | fn no_elided_lt() -> impl use<'static> Sized {}
-   |                               ~~~~~~~
+LL | fn no_elided_lt() -> impl Sized + use<'static> {}
+   |                                       ~~~~~~~
 
 error[E0261]: use of undeclared lifetime name `'missing`
-  --> $DIR/bad-lifetimes.rs:11:29
+  --> $DIR/bad-lifetimes.rs:10:37
    |
-LL | fn missing_lt() -> impl use<'missing> Sized {}
-   |              -              ^^^^^^^^ undeclared lifetime
+LL | fn missing_lt() -> impl Sized + use<'missing> {}
+   |              -                      ^^^^^^^^ undeclared lifetime
    |              |
    |              help: consider introducing lifetime `'missing` here: `<'missing>`
 
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/bad-lifetimes.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: expected lifetime parameter in `use<...>` precise captures list, found `'_`
-  --> $DIR/bad-lifetimes.rs:4:31
+  --> $DIR/bad-lifetimes.rs:3:39
    |
-LL | fn no_elided_lt() -> impl use<'_> Sized {}
-   |                               ^^
+LL | fn no_elided_lt() -> impl Sized + use<'_> {}
+   |                                       ^^
 
 error: expected lifetime parameter in `use<...>` precise captures list, found `'static`
-  --> $DIR/bad-lifetimes.rs:8:28
+  --> $DIR/bad-lifetimes.rs:7:36
    |
-LL | fn static_lt() -> impl use<'static> Sized {}
-   |                            ^^^^^^^
+LL | fn static_lt() -> impl Sized + use<'static> {}
+   |                                    ^^^^^^^
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0106, E0261.
 For more information about an error, try `rustc --explain E0106`.
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.rs b/tests/ui/impl-trait/precise-capturing/bad-params.rs
index 7970d49bf7c..08eee67c0e5 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.rs
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.rs
@@ -1,19 +1,18 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn missing() -> impl use<T> Sized {}
+fn missing() -> impl Sized + use<T> {}
 //~^ ERROR cannot find type `T` in this scope
 
-fn missing_self() -> impl use<Self> Sized {}
+fn missing_self() -> impl Sized + use<Self> {}
 //~^ ERROR cannot find type `Self` in this scope
 
 struct MyType;
 impl MyType {
-    fn self_is_not_param() -> impl use<Self> Sized {}
+    fn self_is_not_param() -> impl Sized + use<Self> {}
     //~^ ERROR `Self` can't be captured in `use<...>` precise captures list, since it is an alias
 }
 
-fn hello() -> impl use<hello> Sized {}
+fn hello() -> impl Sized + use<hello> {}
 //~^ ERROR expected type or const parameter in `use<...>` precise captures list, found function
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/bad-params.stderr b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
index 27bf05302f9..e104f115aa3 100644
--- a/tests/ui/impl-trait/precise-capturing/bad-params.stderr
+++ b/tests/ui/impl-trait/precise-capturing/bad-params.stderr
@@ -1,46 +1,37 @@
 error[E0412]: cannot find type `T` in this scope
-  --> $DIR/bad-params.rs:4:26
+  --> $DIR/bad-params.rs:3:34
    |
-LL | fn missing() -> impl use<T> Sized {}
-   |                          ^ not found in this scope
+LL | fn missing() -> impl Sized + use<T> {}
+   |                                  ^ not found in this scope
    |
 help: you might be missing a type parameter
    |
-LL | fn missing<T>() -> impl use<T> Sized {}
+LL | fn missing<T>() -> impl Sized + use<T> {}
    |           +++
 
 error[E0411]: cannot find type `Self` in this scope
-  --> $DIR/bad-params.rs:7:31
+  --> $DIR/bad-params.rs:6:39
    |
-LL | fn missing_self() -> impl use<Self> Sized {}
-   |    ------------               ^^^^ `Self` is only available in impls, traits, and type definitions
+LL | fn missing_self() -> impl Sized + use<Self> {}
+   |    ------------                       ^^^^ `Self` is only available in impls, traits, and type definitions
    |    |
    |    `Self` not allowed in a function
 
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/bad-params.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: `Self` can't be captured in `use<...>` precise captures list, since it is an alias
-  --> $DIR/bad-params.rs:12:40
+  --> $DIR/bad-params.rs:11:48
    |
 LL | impl MyType {
    | ----------- `Self` is not a generic argument, but an alias to the type of the implementation
-LL |     fn self_is_not_param() -> impl use<Self> Sized {}
-   |                                        ^^^^
+LL |     fn self_is_not_param() -> impl Sized + use<Self> {}
+   |                                                ^^^^
 
 error: expected type or const parameter in `use<...>` precise captures list, found function
-  --> $DIR/bad-params.rs:16:24
+  --> $DIR/bad-params.rs:15:32
    |
-LL | fn hello() -> impl use<hello> Sized {}
-   |                        ^^^^^
+LL | fn hello() -> impl Sized + use<hello> {}
+   |                                ^^^^^
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0411, E0412.
 For more information about an error, try `rustc --explain E0411`.
diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs
index 35b28d0e6fb..82b953bfed4 100644
--- a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs
+++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.rs
@@ -1,5 +1,4 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
 trait Tr {
     type Assoc;
@@ -13,25 +12,25 @@ impl Tr for W<'_> {
 
 // The normal way of capturing `'a`...
 impl<'a> W<'a> {
-    fn good1() -> impl use<'a> Into<<W<'a> as Tr>::Assoc> {}
+    fn good1() -> impl Into<<W<'a> as Tr>::Assoc> + use<'a> {}
 }
 
 // This ensures that we don't error when we capture the *parent* copy of `'a`,
 // since the opaque captures that rather than the duplicated `'a` lifetime
 // synthesized from mentioning `'a` directly in the bounds.
 impl<'a> W<'a> {
-    fn good2() -> impl use<'a> Into<<Self as Tr>::Assoc> {}
+    fn good2() -> impl Into<<Self as Tr>::Assoc> + use<'a> {}
 }
 
 // The normal way of capturing `'a`... but not mentioned in the bounds.
 impl<'a> W<'a> {
-    fn bad1() -> impl use<> Into<<W<'a> as Tr>::Assoc> {}
+    fn bad1() -> impl Into<<W<'a> as Tr>::Assoc> + use<> {}
     //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
 }
 
 // But also make sure that we error here...
 impl<'a> W<'a> {
-    fn bad2() -> impl use<> Into<<Self as Tr>::Assoc> {}
+    fn bad2() -> impl Into<<Self as Tr>::Assoc> + use<> {}
     //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
 }
 
diff --git a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr
index 13aaa999707..b521ee0a902 100644
--- a/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr
+++ b/tests/ui/impl-trait/precise-capturing/capture-parent-arg.stderr
@@ -1,27 +1,18 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/capture-parent-arg.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
-  --> $DIR/capture-parent-arg.rs:28:37
+  --> $DIR/capture-parent-arg.rs:27:31
    |
 LL | impl<'a> W<'a> {
    |      -- this lifetime parameter is captured
-LL |     fn bad1() -> impl use<> Into<<W<'a> as Tr>::Assoc> {}
-   |                  -------------------^^---------------- lifetime captured due to being mentioned in the bounds of the `impl Trait`
+LL |     fn bad1() -> impl Into<<W<'a> as Tr>::Assoc> + use<> {}
+   |                  -------------^^------------------------ lifetime captured due to being mentioned in the bounds of the `impl Trait`
 
 error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
-  --> $DIR/capture-parent-arg.rs:34:18
+  --> $DIR/capture-parent-arg.rs:33:18
    |
 LL | impl<'a> W<'a> {
    |      -- this lifetime parameter is captured
-LL |     fn bad2() -> impl use<> Into<<Self as Tr>::Assoc> {}
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait`
+LL |     fn bad2() -> impl Into<<Self as Tr>::Assoc> + use<> {}
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ lifetime captured due to being mentioned in the bounds of the `impl Trait`
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr b/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr
new file mode 100644
index 00000000000..d8edd672b48
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/duplicated-use.real.stderr
@@ -0,0 +1,8 @@
+error: duplicate `use<...>` precise capturing syntax
+  --> $DIR/duplicated-use.rs:7:32
+   |
+LL | fn hello<'a>() -> impl Sized + use<'a> + use<'a> {}
+   |                                ^^^^^^^   ------- second `use<...>` here
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/impl-trait/precise-capturing/duplicated-use.rs b/tests/ui/impl-trait/precise-capturing/duplicated-use.rs
new file mode 100644
index 00000000000..bfbdcdbf311
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/duplicated-use.rs
@@ -0,0 +1,10 @@
+//@ revisions: real pre_expansion
+//@[pre_expansion] check-pass
+
+#![feature(precise_capturing)]
+
+#[cfg(real)]
+fn hello<'a>() -> impl Sized + use<'a> + use<'a> {}
+//[real]~^ ERROR duplicate `use<...>` precise capturing syntax
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/dyn-use.rs b/tests/ui/impl-trait/precise-capturing/dyn-use.rs
new file mode 100644
index 00000000000..ce7a0f3c7b2
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/dyn-use.rs
@@ -0,0 +1,4 @@
+#![feature(precise_capturing)]
+
+fn dyn() -> &'static dyn use<> { &() }
+//~^ ERROR expected one of `!`, `(`, `::`, `<`, `where`, or `{`, found keyword `use`
diff --git a/tests/ui/impl-trait/precise-capturing/dyn-use.stderr b/tests/ui/impl-trait/precise-capturing/dyn-use.stderr
new file mode 100644
index 00000000000..5519633de1f
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/dyn-use.stderr
@@ -0,0 +1,8 @@
+error: expected one of `!`, `(`, `::`, `<`, `where`, or `{`, found keyword `use`
+  --> $DIR/dyn-use.rs:3:26
+   |
+LL | fn dyn() -> &'static dyn use<> { &() }
+   |                          ^^^ expected one of `!`, `(`, `::`, `<`, `where`, or `{`
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/impl-trait/precise-capturing/elided.rs b/tests/ui/impl-trait/precise-capturing/elided.rs
index de80e8a5d58..34d1ba620dc 100644
--- a/tests/ui/impl-trait/precise-capturing/elided.rs
+++ b/tests/ui/impl-trait/precise-capturing/elided.rs
@@ -1,8 +1,7 @@
 //@ check-pass
 
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn elided(x: &()) -> impl use<'_> Sized { x }
+fn elided(x: &()) -> impl Sized + use<'_> { x }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/elided.stderr b/tests/ui/impl-trait/precise-capturing/elided.stderr
deleted file mode 100644
index 38da0828de9..00000000000
--- a/tests/ui/impl-trait/precise-capturing/elided.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/elided.rs:3:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
index 1b604e6c358..26d29e456ea 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.rs
@@ -1,7 +1,6 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn constant<const C: usize>() -> impl use<> Sized {}
+fn constant<const C: usize>() -> impl Sized + use<> {}
 //~^ ERROR `impl Trait` must mention all const parameters in scope
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
index 3f78e7c56b6..989ed136d4c 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-const.stderr
@@ -1,21 +1,12 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/forgot-to-capture-const.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: `impl Trait` must mention all const parameters in scope in `use<...>`
-  --> $DIR/forgot-to-capture-const.rs:4:34
+  --> $DIR/forgot-to-capture-const.rs:3:34
    |
-LL | fn constant<const C: usize>() -> impl use<> Sized {}
-   |             --------------       ^^^^^^^^^^^^^^^^
+LL | fn constant<const C: usize>() -> impl Sized + use<> {}
+   |             --------------       ^^^^^^^^^^^^^^^^^^
    |             |
    |             const parameter is implicitly captured by this `impl Trait`
    |
    = note: currently, all const parameters are required to be mentioned in the precise captures list
 
-error: aborting due to 1 previous error; 1 warning emitted
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
index cc86bf83107..f18dbca6c5e 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.rs
@@ -1,10 +1,9 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
+fn lifetime_in_bounds<'a>(x: &'a ()) -> impl Into<&'a ()> + use<> { x }
 //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
 
-fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
+fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> { x }
 //~^ ERROR hidden type for `impl Sized` captures lifetime that does not appear in bounds
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
index e472c898050..6544837ba83 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-lifetime.stderr
@@ -1,35 +1,26 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/forgot-to-capture-lifetime.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list
-  --> $DIR/forgot-to-capture-lifetime.rs:4:58
+  --> $DIR/forgot-to-capture-lifetime.rs:3:52
    |
-LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl use<> Into<&'a ()> { x }
-   |                       --                -----------------^^----
+LL | fn lifetime_in_bounds<'a>(x: &'a ()) -> impl Into<&'a ()> + use<> { x }
+   |                       --                -----------^^------------
    |                       |                 |
    |                       |                 lifetime captured due to being mentioned in the bounds of the `impl Trait`
    |                       this lifetime parameter is captured
 
 error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds
-  --> $DIR/forgot-to-capture-lifetime.rs:7:60
+  --> $DIR/forgot-to-capture-lifetime.rs:6:62
    |
-LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized { x }
-   |                       --                ----------------   ^
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> { x }
+   |                       --                ------------------   ^
    |                       |                 |
    |                       |                 opaque type defined here
    |                       hidden type `&'a ()` captures the lifetime `'a` as defined here
    |
 help: to declare that `impl Sized` captures `'a`, you can add an explicit `'a` lifetime bound
    |
-LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl use<> Sized + 'a { x }
-   |                                                          ++++
+LL | fn lifetime_in_hidden<'a>(x: &'a ()) -> impl Sized + use<> + 'a { x }
+   |                                                            ++++
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
index d359ea5e26d..08014985783 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.rs
@@ -1,11 +1,10 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn type_param<T>() -> impl use<> Sized {}
+fn type_param<T>() -> impl Sized + use<> {}
 //~^ ERROR `impl Trait` must mention all type parameters in scope
 
 trait Foo {
-    fn bar() -> impl use<> Sized;
+    fn bar() -> impl Sized + use<>;
     //~^ ERROR `impl Trait` must mention the `Self` type of the trait
 }
 
diff --git a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
index 26994d0bdbf..93b44a0c18c 100644
--- a/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
+++ b/tests/ui/impl-trait/precise-capturing/forgot-to-capture-type.stderr
@@ -1,31 +1,22 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/forgot-to-capture-type.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: `impl Trait` must mention all type parameters in scope in `use<...>`
-  --> $DIR/forgot-to-capture-type.rs:4:23
+  --> $DIR/forgot-to-capture-type.rs:3:23
    |
-LL | fn type_param<T>() -> impl use<> Sized {}
-   |               -       ^^^^^^^^^^^^^^^^
+LL | fn type_param<T>() -> impl Sized + use<> {}
+   |               -       ^^^^^^^^^^^^^^^^^^
    |               |
    |               type parameter is implicitly captured by this `impl Trait`
    |
    = note: currently, all type parameters are required to be mentioned in the precise captures list
 
 error: `impl Trait` must mention the `Self` type of the trait in `use<...>`
-  --> $DIR/forgot-to-capture-type.rs:8:17
+  --> $DIR/forgot-to-capture-type.rs:7:17
    |
 LL | trait Foo {
    | --------- `Self` type parameter is implicitly captured by this `impl Trait`
-LL |     fn bar() -> impl use<> Sized;
-   |                 ^^^^^^^^^^^^^^^^
+LL |     fn bar() -> impl Sized + use<>;
+   |                 ^^^^^^^^^^^^^^^^^^
    |
    = note: currently, all type parameters are required to be mentioned in the precise captures list
 
-error: aborting due to 2 previous errors; 1 warning emitted
+error: aborting due to 2 previous errors
 
diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
index 28fb1fa4b9e..21ac19640bc 100644
--- a/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
+++ b/tests/ui/impl-trait/precise-capturing/higher-ranked.rs
@@ -3,7 +3,6 @@
 // Show how precise captures allow us to skip capturing a higher-ranked lifetime
 
 #![feature(lifetime_capture_rules_2024, precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
 trait Trait<'a> {
     type Item;
@@ -13,6 +12,6 @@ impl Trait<'_> for () {
     type Item = Vec<()>;
 }
 
-fn hello() -> impl for<'a> Trait<'a, Item = impl use<> IntoIterator> {}
+fn hello() -> impl for<'a> Trait<'a, Item = impl IntoIterator + use<>> {}
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr b/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr
deleted file mode 100644
index e48d6d42af0..00000000000
--- a/tests/ui/impl-trait/precise-capturing/higher-ranked.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/higher-ranked.rs:5:41
-   |
-LL | #![feature(lifetime_capture_rules_2024, precise_capturing)]
-   |                                         ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr b/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr
new file mode 100644
index 00000000000..2b234bcb6a5
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/illegal-positions.real.stderr
@@ -0,0 +1,57 @@
+error: `use<...>` precise capturing syntax not allowed in supertrait bounds
+  --> $DIR/illegal-positions.rs:8:12
+   |
+LL | trait Foo: use<> {
+   |            ^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in bounds
+  --> $DIR/illegal-positions.rs:10:33
+   |
+LL |     type Assoc: use<> where (): use<>;
+   |                                 ^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in bounds
+  --> $DIR/illegal-positions.rs:10:17
+   |
+LL |     type Assoc: use<> where (): use<>;
+   |                 ^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in bounds
+  --> $DIR/illegal-positions.rs:16:11
+   |
+LL | fn fun<T: use<>>(_: impl use<>) where (): use<> {}
+   |           ^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in bounds
+  --> $DIR/illegal-positions.rs:16:43
+   |
+LL | fn fun<T: use<>>(_: impl use<>) where (): use<> {}
+   |                                           ^^^^^
+
+error: at least one trait must be specified
+  --> $DIR/illegal-positions.rs:16:21
+   |
+LL | fn fun<T: use<>>(_: impl use<>) where (): use<> {}
+   |                     ^^^^^^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in `dyn` trait object bounds
+  --> $DIR/illegal-positions.rs:23:25
+   |
+LL | fn dynamic() -> Box<dyn use<>> {}
+   |                         ^^^^^
+
+error: `use<...>` precise capturing syntax not allowed in argument-position `impl Trait`
+  --> $DIR/illegal-positions.rs:16:26
+   |
+LL | fn fun<T: use<>>(_: impl use<>) where (): use<> {}
+   |                          ^^^^^
+
+error[E0224]: at least one trait is required for an object type
+  --> $DIR/illegal-positions.rs:23:21
+   |
+LL | fn dynamic() -> Box<dyn use<>> {}
+   |                     ^^^^^^^^^
+
+error: aborting due to 9 previous errors
+
+For more information about this error, try `rustc --explain E0224`.
diff --git a/tests/ui/impl-trait/precise-capturing/illegal-positions.rs b/tests/ui/impl-trait/precise-capturing/illegal-positions.rs
new file mode 100644
index 00000000000..681458e25f8
--- /dev/null
+++ b/tests/ui/impl-trait/precise-capturing/illegal-positions.rs
@@ -0,0 +1,27 @@
+//@ revisions: real pre_expansion
+//@[pre_expansion] check-pass
+//@ edition: 2021
+
+#![feature(precise_capturing)]
+
+#[cfg(real)]
+trait Foo: use<> {
+    //[real]~^ ERROR `use<...>` precise capturing syntax not allowed
+    type Assoc: use<> where (): use<>;
+    //[real]~^ ERROR `use<...>` precise capturing syntax not allowed
+    //[real]~| ERROR `use<...>` precise capturing syntax not allowed
+}
+
+#[cfg(real)]
+fn fun<T: use<>>(_: impl use<>) where (): use<> {}
+//[real]~^ ERROR `use<...>` precise capturing syntax not allowed
+//[real]~| ERROR `use<...>` precise capturing syntax not allowed
+//[real]~| ERROR `use<...>` precise capturing syntax not allowed
+//[real]~| ERROR at least one trait must be specified
+
+#[cfg(real)]
+fn dynamic() -> Box<dyn use<>> {}
+//[real]~^ ERROR `use<...>` precise capturing syntax not allowed in `dyn` trait object bounds
+//[real]~| ERROR at least one trait is required for an object type [E0224]
+
+fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/ordering.rs b/tests/ui/impl-trait/precise-capturing/ordering.rs
index 2bace798c57..eb570a120cc 100644
--- a/tests/ui/impl-trait/precise-capturing/ordering.rs
+++ b/tests/ui/impl-trait/precise-capturing/ordering.rs
@@ -1,16 +1,15 @@
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn lt<'a>() -> impl use<'a, 'a> Sized {}
+fn lt<'a>() -> impl Sized + use<'a, 'a> {}
 //~^ ERROR cannot capture parameter `'a` twice
 
-fn ty<T>() -> impl use<T, T> Sized {}
+fn ty<T>() -> impl Sized + use<T, T> {}
 //~^ ERROR cannot capture parameter `T` twice
 
-fn ct<const N: usize>() -> impl use<N, N> Sized {}
+fn ct<const N: usize>() -> impl Sized + use<N, N> {}
 //~^ ERROR cannot capture parameter `N` twice
 
-fn ordering<'a, T>() -> impl use<T, 'a> Sized {}
+fn ordering<'a, T>() -> impl Sized + use<T, 'a> {}
 //~^ ERROR lifetime parameter `'a` must be listed before non-lifetime parameters
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/ordering.stderr b/tests/ui/impl-trait/precise-capturing/ordering.stderr
index 3f545108df5..ecd47159059 100644
--- a/tests/ui/impl-trait/precise-capturing/ordering.stderr
+++ b/tests/ui/impl-trait/precise-capturing/ordering.stderr
@@ -1,37 +1,28 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/ordering.rs:1:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 error: cannot capture parameter `'a` twice
-  --> $DIR/ordering.rs:4:25
+  --> $DIR/ordering.rs:3:33
    |
-LL | fn lt<'a>() -> impl use<'a, 'a> Sized {}
-   |                         ^^  -- parameter captured again here
+LL | fn lt<'a>() -> impl Sized + use<'a, 'a> {}
+   |                                 ^^  -- parameter captured again here
 
 error: cannot capture parameter `T` twice
-  --> $DIR/ordering.rs:7:24
+  --> $DIR/ordering.rs:6:32
    |
-LL | fn ty<T>() -> impl use<T, T> Sized {}
-   |                        ^  - parameter captured again here
+LL | fn ty<T>() -> impl Sized + use<T, T> {}
+   |                                ^  - parameter captured again here
 
 error: cannot capture parameter `N` twice
-  --> $DIR/ordering.rs:10:37
+  --> $DIR/ordering.rs:9:45
    |
-LL | fn ct<const N: usize>() -> impl use<N, N> Sized {}
-   |                                     ^  - parameter captured again here
+LL | fn ct<const N: usize>() -> impl Sized + use<N, N> {}
+   |                                             ^  - parameter captured again here
 
 error: lifetime parameter `'a` must be listed before non-lifetime parameters
-  --> $DIR/ordering.rs:13:37
+  --> $DIR/ordering.rs:12:45
    |
-LL | fn ordering<'a, T>() -> impl use<T, 'a> Sized {}
-   |                                  -  ^^
-   |                                  |
-   |                                  move the lifetime before this parameter
+LL | fn ordering<'a, T>() -> impl Sized + use<T, 'a> {}
+   |                                          -  ^^
+   |                                          |
+   |                                          move the lifetime before this parameter
 
-error: aborting due to 4 previous errors; 1 warning emitted
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/impl-trait/precise-capturing/outlives.rs b/tests/ui/impl-trait/precise-capturing/outlives.rs
index 71e6333934e..26ac922b5b9 100644
--- a/tests/ui/impl-trait/precise-capturing/outlives.rs
+++ b/tests/ui/impl-trait/precise-capturing/outlives.rs
@@ -3,9 +3,8 @@
 // Show that precise captures allow us to skip a lifetime param for outlives
 
 #![feature(lifetime_capture_rules_2024, precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn hello<'a: 'a, 'b: 'b>() -> impl use<'a> Sized { }
+fn hello<'a: 'a, 'b: 'b>() -> impl Sized + use<'a> { }
 
 fn outlives<'a, T: 'a>(_: T) {}
 
diff --git a/tests/ui/impl-trait/precise-capturing/outlives.stderr b/tests/ui/impl-trait/precise-capturing/outlives.stderr
deleted file mode 100644
index 405c09cccd9..00000000000
--- a/tests/ui/impl-trait/precise-capturing/outlives.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/outlives.rs:5:41
-   |
-LL | #![feature(lifetime_capture_rules_2024, precise_capturing)]
-   |                                         ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed
index 014ab23e4eb..5ac296a9cbd 100644
--- a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed
+++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.fixed
@@ -4,15 +4,15 @@
 #![allow(unused, incomplete_features)]
 #![deny(impl_trait_overcaptures)]
 
-fn named<'a>(x: &'a i32) -> impl use<> Sized { *x }
+fn named<'a>(x: &'a i32) -> impl Sized + use<> { *x }
 //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
 
-fn implicit(x: &i32) -> impl use<> Sized { *x }
+fn implicit(x: &i32) -> impl Sized + use<> { *x }
 //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
 
 struct W;
 impl W {
-    fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self }
+    fn hello(&self, x: &i32) -> impl Sized + '_ + use<'_> { self }
     //~^ ERROR `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024
 }
 
@@ -23,7 +23,7 @@ impl Higher<'_> for () {
     type Output = ();
 }
 
-fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {}
+fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized + use<>> {}
 //~^ ERROR `impl Sized` will capture more lifetimes than possibly intended in edition 2024
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr
index 16cb8b7e94b..f8bb7f099af 100644
--- a/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr
+++ b/tests/ui/impl-trait/precise-capturing/overcaptures-2024.stderr
@@ -17,8 +17,8 @@ LL | #![deny(impl_trait_overcaptures)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
-LL | fn named<'a>(x: &'a i32) -> impl use<> Sized { *x }
-   |                                  +++++
+LL | fn named<'a>(x: &'a i32) -> impl Sized + use<> { *x }
+   |                                        +++++++
 
 error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024
   --> $DIR/overcaptures-2024.rs:10:25
@@ -34,8 +34,8 @@ LL | fn implicit(x: &i32) -> impl Sized { *x }
    = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
-LL | fn implicit(x: &i32) -> impl use<> Sized { *x }
-   |                              +++++
+LL | fn implicit(x: &i32) -> impl Sized + use<> { *x }
+   |                                    +++++++
 
 error: `impl Sized + '_` will capture more lifetimes than possibly intended in edition 2024
   --> $DIR/overcaptures-2024.rs:15:33
@@ -51,8 +51,8 @@ LL |     fn hello(&self, x: &i32) -> impl Sized + '_ { self }
    = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
-LL |     fn hello(&self, x: &i32) -> impl use<'_> Sized + '_ { self }
-   |                                      +++++++
+LL |     fn hello(&self, x: &i32) -> impl Sized + '_ + use<'_> { self }
+   |                                                 +++++++++
 
 error: `impl Sized` will capture more lifetimes than possibly intended in edition 2024
   --> $DIR/overcaptures-2024.rs:26:47
@@ -68,8 +68,8 @@ LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized> {}
    = note: all lifetimes in scope will be captured by `impl Trait`s in edition 2024
 help: use the precise capturing `use<...>` syntax to make the captures explicit
    |
-LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl use<> Sized> {}
-   |                                                    +++++
+LL | fn hrtb() -> impl for<'a> Higher<'a, Output = impl Sized + use<>> {}
+   |                                                          +++++++
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.rs b/tests/ui/impl-trait/precise-capturing/redundant.rs
index 108a4cb64aa..99c128fdc48 100644
--- a/tests/ui/impl-trait/precise-capturing/redundant.rs
+++ b/tests/ui/impl-trait/precise-capturing/redundant.rs
@@ -2,23 +2,22 @@
 //@ check-pass
 
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
-fn hello<'a>() -> impl use<'a> Sized {}
+fn hello<'a>() -> impl Sized + use<'a> {}
 //~^ WARN all possible in-scope parameters are already captured
 
 struct Inherent;
 impl Inherent {
-    fn inherent(&self) -> impl use<'_> Sized {}
+    fn inherent(&self) -> impl Sized + use<'_> {}
     //~^ WARN all possible in-scope parameters are already captured
 }
 
 trait Test<'a> {
-    fn in_trait() -> impl use<'a, Self> Sized;
+    fn in_trait() -> impl Sized + use<'a, Self>;
     //~^ WARN all possible in-scope parameters are already captured
 }
 impl<'a> Test<'a> for () {
-    fn in_trait() -> impl use<'a> Sized {}
+    fn in_trait() -> impl Sized + use<'a> {}
     //~^ WARN all possible in-scope parameters are already captured
 }
 
diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr
index 325f04d3536..274d9d2375f 100644
--- a/tests/ui/impl-trait/precise-capturing/redundant.stderr
+++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr
@@ -1,45 +1,36 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/redundant.rs:4:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
 warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:7:19
+  --> $DIR/redundant.rs:6:19
    |
-LL | fn hello<'a>() -> impl use<'a> Sized {}
-   |                   ^^^^^-------^^^^^^
-   |                        |
-   |                        help: remove the `use<...>` syntax
+LL | fn hello<'a>() -> impl Sized + use<'a> {}
+   |                   ^^^^^^^^^^^^^-------
+   |                                |
+   |                                help: remove the `use<...>` syntax
    |
    = note: `#[warn(impl_trait_redundant_captures)]` on by default
 
 warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:12:27
+  --> $DIR/redundant.rs:11:27
    |
-LL |     fn inherent(&self) -> impl use<'_> Sized {}
-   |                           ^^^^^-------^^^^^^
-   |                                |
-   |                                help: remove the `use<...>` syntax
+LL |     fn inherent(&self) -> impl Sized + use<'_> {}
+   |                           ^^^^^^^^^^^^^-------
+   |                                        |
+   |                                        help: remove the `use<...>` syntax
 
 warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:17:22
+  --> $DIR/redundant.rs:16:22
    |
-LL |     fn in_trait() -> impl use<'a, Self> Sized;
-   |                      ^^^^^-------------^^^^^^
-   |                           |
-   |                           help: remove the `use<...>` syntax
+LL |     fn in_trait() -> impl Sized + use<'a, Self>;
+   |                      ^^^^^^^^^^^^^-------------
+   |                                   |
+   |                                   help: remove the `use<...>` syntax
 
 warning: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant
-  --> $DIR/redundant.rs:21:22
+  --> $DIR/redundant.rs:20:22
    |
-LL |     fn in_trait() -> impl use<'a> Sized {}
-   |                      ^^^^^-------^^^^^^
-   |                           |
-   |                           help: remove the `use<...>` syntax
+LL |     fn in_trait() -> impl Sized + use<'a> {}
+   |                      ^^^^^^^^^^^^^-------
+   |                                   |
+   |                                   help: remove the `use<...>` syntax
 
-warning: 5 warnings emitted
+warning: 4 warnings emitted
 
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.rs b/tests/ui/impl-trait/precise-capturing/self-capture.rs
index ecbc388e27b..e0a4a8b658c 100644
--- a/tests/ui/impl-trait/precise-capturing/self-capture.rs
+++ b/tests/ui/impl-trait/precise-capturing/self-capture.rs
@@ -1,10 +1,9 @@
 //@ check-pass
 
 #![feature(precise_capturing)]
-//~^ WARN the feature `precise_capturing` is incomplete
 
 trait Foo {
-    fn bar<'a>() -> impl use<Self> Sized;
+    fn bar<'a>() -> impl Sized + use<Self>;
 }
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/self-capture.stderr b/tests/ui/impl-trait/precise-capturing/self-capture.stderr
deleted file mode 100644
index 5a058c6826d..00000000000
--- a/tests/ui/impl-trait/precise-capturing/self-capture.stderr
+++ /dev/null
@@ -1,11 +0,0 @@
-warning: the feature `precise_capturing` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/self-capture.rs:3:12
-   |
-LL | #![feature(precise_capturing)]
-   |            ^^^^^^^^^^^^^^^^^
-   |
-   = note: see issue #123432 <https://github.com/rust-lang/rust/issues/123432> for more information
-   = note: `#[warn(incomplete_features)]` on by default
-
-warning: 1 warning emitted
-
diff --git a/tests/ui/impl-trait/precise-capturing/unexpected-token.rs b/tests/ui/impl-trait/precise-capturing/unexpected-token.rs
index 39c8c0def6b..a1089fd7bfc 100644
--- a/tests/ui/impl-trait/precise-capturing/unexpected-token.rs
+++ b/tests/ui/impl-trait/precise-capturing/unexpected-token.rs
@@ -2,8 +2,7 @@
 // token due to a strange interaction between the sequence parsing code and the
 // param/lifetime parsing code.
 
-fn hello() -> impl use<'a {}> Sized {}
+fn hello() -> impl Sized + use<'a {}> {}
 //~^ ERROR expected one of `,` or `>`, found `{`
-//~| ERROR expected item, found `>`
 
 fn main() {}
diff --git a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr
index 989c479b248..51e4f0f6775 100644
--- a/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr
+++ b/tests/ui/impl-trait/precise-capturing/unexpected-token.stderr
@@ -1,16 +1,8 @@
 error: expected one of `,` or `>`, found `{`
-  --> $DIR/unexpected-token.rs:5:27
+  --> $DIR/unexpected-token.rs:5:35
    |
-LL | fn hello() -> impl use<'a {}> Sized {}
-   |                           ^ expected one of `,` or `>`
+LL | fn hello() -> impl Sized + use<'a {}> {}
+   |                                   ^ expected one of `,` or `>`
 
-error: expected item, found `>`
-  --> $DIR/unexpected-token.rs:5:29
-   |
-LL | fn hello() -> impl use<'a {}> Sized {}
-   |                             ^ expected item
-   |
-   = note: for a full list of items that can appear in modules, see <https://doc.rust-lang.org/reference/items.html>
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/tests/ui/instrument-coverage/coverage-options.bad.stderr b/tests/ui/instrument-coverage/coverage-options.bad.stderr
index 4a272cf97fb..1a6b30dc832 100644
--- a/tests/ui/instrument-coverage/coverage-options.bad.stderr
+++ b/tests/ui/instrument-coverage/coverage-options.bad.stderr
@@ -1,2 +1,2 @@
-error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` was expected
+error: incorrect value `bad` for unstable option `coverage-options` - `block` | `branch` | `condition` | `mcdc` | `no-mir-spans` was expected
 
diff --git a/tests/ui/issues/issue-14915.stderr b/tests/ui/issues/issue-14915.stderr
index 279f5772d21..3558bd651c6 100644
--- a/tests/ui/issues/issue-14915.stderr
+++ b/tests/ui/issues/issue-14915.stderr
@@ -5,6 +5,12 @@ LL |     println!("{}", x + 1);
    |                    - ^ - {integer}
    |                    |
    |                    Box<isize>
+   |
+note: the foreign item type `Box<isize>` doesn't implement `Add<{integer}>`
+  --> $SRC_DIR/alloc/src/boxed.rs:LL:COL
+  ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL
+   |
+   = note: not implement `Add<{integer}>`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/issues/issue-66706.rs b/tests/ui/issues/issue-66706.rs
index 6d1d9f5e3a9..87dc3437a57 100644
--- a/tests/ui/issues/issue-66706.rs
+++ b/tests/ui/issues/issue-66706.rs
@@ -1,6 +1,6 @@
 fn a() {
     [0; [|_: _ &_| ()].len()]
-    //~^ ERROR expected `,`, found `&`
+    //~^ ERROR expected one of `,` or `|`, found `&`
     //~| ERROR type annotations needed
 }
 
@@ -11,7 +11,7 @@ fn b() {
 
 fn c() {
     [0; [|&_: _ &_| {}; 0 ].len()]
-    //~^ ERROR expected `,`, found `&`
+    //~^ ERROR expected one of `,` or `|`, found `&`
     //~| ERROR type annotations needed
 }
 
diff --git a/tests/ui/issues/issue-66706.stderr b/tests/ui/issues/issue-66706.stderr
index 0271db754bd..dd1e07589f5 100644
--- a/tests/ui/issues/issue-66706.stderr
+++ b/tests/ui/issues/issue-66706.stderr
@@ -1,8 +1,8 @@
-error: expected `,`, found `&`
+error: expected one of `,` or `|`, found `&`
   --> $DIR/issue-66706.rs:2:16
    |
 LL |     [0; [|_: _ &_| ()].len()]
-   |               -^ expected `,`
+   |               -^ expected one of `,` or `|`
    |               |
    |               help: missing `,`
 
@@ -12,11 +12,11 @@ error: expected identifier, found reserved identifier `_`
 LL |     [0; [|f @ &ref _| {} ; 0 ].len() ];
    |                    ^ expected identifier, found reserved identifier
 
-error: expected `,`, found `&`
+error: expected one of `,` or `|`, found `&`
   --> $DIR/issue-66706.rs:13:17
    |
 LL |     [0; [|&_: _ &_| {}; 0 ].len()]
-   |                -^ expected `,`
+   |                -^ expected one of `,` or `|`
    |                |
    |                help: missing `,`
 
diff --git a/tests/ui/minus-string.stderr b/tests/ui/minus-string.stderr
index 105274ee7d0..cf63ec24416 100644
--- a/tests/ui/minus-string.stderr
+++ b/tests/ui/minus-string.stderr
@@ -3,6 +3,11 @@ error[E0600]: cannot apply unary operator `-` to type `String`
    |
 LL | fn main() { -"foo".to_string(); }
    |             ^^^^^^^^^^^^^^^^^^ cannot apply unary operator `-`
+   |
+note: the foreign item type `String` doesn't implement `Neg`
+  --> $SRC_DIR/alloc/src/string.rs:LL:COL
+   |
+   = note: not implement `Neg`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/trait-object-delimiters.rs b/tests/ui/parser/trait-object-delimiters.rs
index d6bc629aa11..8f6221c1b94 100644
--- a/tests/ui/parser/trait-object-delimiters.rs
+++ b/tests/ui/parser/trait-object-delimiters.rs
@@ -8,7 +8,7 @@ fn foo2(_: &dyn (Drop + AsRef<str>)) {} //~ ERROR incorrect parentheses around t
 fn foo2_no_space(_: &dyn(Drop + AsRef<str>)) {} //~ ERROR incorrect parentheses around trait bounds
 
 fn foo3(_: &dyn {Drop + AsRef<str>}) {} //~ ERROR expected parameter name, found `{`
-//~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `~`, lifetime, or path, found `{`
+//~^ ERROR expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `use`, `~`, lifetime, or path, found `{`
 //~| ERROR at least one trait is required for an object type
 
 fn foo4(_: &dyn <Drop + AsRef<str>>) {} //~ ERROR expected identifier, found `<`
diff --git a/tests/ui/parser/trait-object-delimiters.stderr b/tests/ui/parser/trait-object-delimiters.stderr
index 2b1f8df991f..5f175e86545 100644
--- a/tests/ui/parser/trait-object-delimiters.stderr
+++ b/tests/ui/parser/trait-object-delimiters.stderr
@@ -34,11 +34,11 @@ error: expected parameter name, found `{`
 LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
    |                 ^ expected parameter name
 
-error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `~`, lifetime, or path, found `{`
+error: expected one of `!`, `(`, `)`, `*`, `,`, `?`, `async`, `const`, `for`, `use`, `~`, lifetime, or path, found `{`
   --> $DIR/trait-object-delimiters.rs:10:17
    |
 LL | fn foo3(_: &dyn {Drop + AsRef<str>}) {}
-   |                -^ expected one of 12 possible tokens
+   |                -^ expected one of 13 possible tokens
    |                |
    |                help: missing `,`
 
diff --git a/tests/ui/pattern/pattern-tyvar-2.stderr b/tests/ui/pattern/pattern-tyvar-2.stderr
index c6540e79558..be52fa8b239 100644
--- a/tests/ui/pattern/pattern-tyvar-2.stderr
+++ b/tests/ui/pattern/pattern-tyvar-2.stderr
@@ -5,6 +5,11 @@ LL | fn foo(t: Bar) -> isize { match t { Bar::T1(_, Some(x)) => { return x * 3;
    |                                                                     - ^ - {integer}
    |                                                                     |
    |                                                                     Vec<isize>
+   |
+note: the foreign item type `Vec<isize>` doesn't implement `Mul<{integer}>`
+  --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+   |
+   = note: not implement `Mul<{integer}>`
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/typeck/assign-non-lval-derefmut.stderr b/tests/ui/typeck/assign-non-lval-derefmut.stderr
index b26d16da015..ce0ff1d957b 100644
--- a/tests/ui/typeck/assign-non-lval-derefmut.stderr
+++ b/tests/ui/typeck/assign-non-lval-derefmut.stderr
@@ -19,6 +19,10 @@ LL |     x.lock().unwrap() += 1;
    |     |
    |     cannot use `+=` on type `MutexGuard<'_, usize>`
    |
+note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 help: `+=` can be used on `usize` if you dereference the left-hand side
    |
 LL |     *x.lock().unwrap() += 1;
@@ -47,6 +51,10 @@ LL |     y += 1;
    |     |
    |     cannot use `+=` on type `MutexGuard<'_, usize>`
    |
+note: the foreign item type `MutexGuard<'_, usize>` doesn't implement `AddAssign<{integer}>`
+  --> $SRC_DIR/std/src/sync/mutex.rs:LL:COL
+   |
+   = note: not implement `AddAssign<{integer}>`
 help: `+=` can be used on `usize` if you dereference the left-hand side
    |
 LL |     *y += 1;