about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-22 05:06:27 +0000
committerbors <bors@rust-lang.org>2024-02-22 05:06:27 +0000
commit3815fc06256f6281ee064fc607d24020dc96f05b (patch)
treea9ec036cc3cb6e18d43f62e85574e81288b82cf0 /compiler
parent596dd65cfa1d5f1a55a9a68d7f45bda436e3055d (diff)
parent6f3bc7d938f5a8a772ba67cf41aad16a03b9fb1c (diff)
Auto merge of #3310 - rust-lang:rustup-2024-02-22, r=oli-obk
Automatic Rustup
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_arena/src/lib.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs9
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs4
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs7
-rw-r--r--compiler/rustc_ast_passes/src/errors.rs17
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs3
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs35
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs8
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs12
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs17
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs17
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs27
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs10
-rw-r--r--compiler/rustc_borrowck/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/canonical.rs2
-rw-r--r--compiler/rustc_borrowck/src/type_check/constraint_conversion.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs19
-rw-r--r--compiler/rustc_borrowck/src/type_check/input_output.rs3
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs18
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs485
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs2
-rw-r--r--compiler/rustc_builtin_macros/src/errors.rs10
-rw-r--r--compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs21
-rw-r--r--compiler/rustc_codegen_gcc/src/builder.rs25
-rw-r--r--compiler/rustc_codegen_llvm/src/builder.rs48
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm/ffi.rs1
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs13
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs2
-rw-r--r--compiler/rustc_codegen_ssa/src/back/metadata.rs88
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/intrinsic.rs32
-rw-r--r--compiler/rustc_codegen_ssa/src/traits/builder.rs5
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs25
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs6
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs3
-rw-r--r--compiler/rustc_const_eval/src/const_eval/valtrees.rs10
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs6
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs7
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs2
-rw-r--r--compiler/rustc_const_eval/src/util/caller_location.rs9
-rw-r--r--compiler/rustc_driver_impl/src/lib.rs39
-rw-r--r--compiler/rustc_driver_impl/src/pretty.rs14
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs624
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs441
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs30
-rw-r--r--compiler/rustc_errors/src/emitter.rs2
-rw-r--r--compiler/rustc_errors/src/lib.rs179
-rw-r--r--compiler/rustc_expand/messages.ftl8
-rw-r--r--compiler/rustc_expand/src/base.rs2
-rw-r--r--compiler/rustc_expand/src/config.rs1
-rw-r--r--compiler/rustc_expand/src/errors.rs30
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs10
-rw-r--r--compiler/rustc_expand/src/proc_macro.rs24
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs9
-rw-r--r--compiler/rustc_hir/src/pat_util.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/bounds.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs6
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/lint.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs34
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs18
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs12
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs29
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs188
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs9
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs53
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs11
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs28
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/callee.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs10
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs24
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs38
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs74
-rw-r--r--compiler/rustc_hir_typeck/src/intrinsicck.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/mem_categorization.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/method/mod.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs57
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs17
-rw-r--r--compiler/rustc_incremental/src/persist/fs.rs2
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs4
-rw-r--r--compiler/rustc_infer/src/errors/mod.rs59
-rw-r--r--compiler/rustc_infer/src/errors/note_and_explain.rs11
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs107
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs33
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs10
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs18
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs16
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs10
-rw-r--r--compiler/rustc_infer/src/infer/relate/combine.rs219
-rw-r--r--compiler/rustc_infer/src/infer/relate/equate.rs18
-rw-r--r--compiler/rustc_infer/src/infer/relate/generalize.rs478
-rw-r--r--compiler/rustc_infer/src/infer/relate/glb.rs5
-rw-r--r--compiler/rustc_infer/src/infer/relate/lub.rs5
-rw-r--r--compiler/rustc_infer/src/infer/relate/mod.rs3
-rw-r--r--compiler/rustc_infer/src/infer/relate/nll.rs720
-rw-r--r--compiler/rustc_infer/src/infer/relate/sub.rs13
-rw-r--r--compiler/rustc_interface/Cargo.toml1
-rw-r--r--compiler/rustc_interface/src/interface.rs37
-rw-r--r--compiler/rustc_interface/src/lib.rs1
-rw-r--r--compiler/rustc_interface/src/passes.rs15
-rw-r--r--compiler/rustc_interface/src/queries.rs16
-rw-r--r--compiler/rustc_interface/src/util.rs33
-rw-r--r--compiler/rustc_lint/messages.ftl5
-rw-r--r--compiler/rustc_lint/src/builtin.rs30
-rw-r--r--compiler/rustc_lint/src/context/diagnostics.rs3
-rw-r--r--compiler/rustc_lint/src/errors.rs10
-rw-r--r--compiler/rustc_lint/src/levels.rs3
-rw-r--r--compiler/rustc_lint/src/lib.rs7
-rw-r--r--compiler/rustc_lint/src/lints.rs78
-rw-r--r--compiler/rustc_lint_defs/src/builtin.rs37
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs30
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp25
-rw-r--r--compiler/rustc_macros/src/diagnostics/subdiagnostic.rs10
-rw-r--r--compiler/rustc_macros/src/lib.rs14
-rw-r--r--compiler/rustc_metadata/messages.ftl2
-rw-r--r--compiler/rustc_metadata/src/creader.rs68
-rw-r--r--compiler/rustc_metadata/src/errors.rs5
-rw-r--r--compiler/rustc_metadata/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/locator.rs8
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs10
-rw-r--r--compiler/rustc_middle/src/arena.rs2
-rw-r--r--compiler/rustc_middle/src/lint.rs8
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs4
-rw-r--r--compiler/rustc_middle/src/query/mod.rs39
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs4
-rw-r--r--compiler/rustc_middle/src/traits/query.rs6
-rw-r--r--compiler/rustc_middle/src/ty/context.rs5
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs8
-rw-r--r--compiler/rustc_middle/src/ty/error.rs2
-rw-r--r--compiler/rustc_middle/src/ty/flags.rs5
-rw-r--r--compiler/rustc_middle/src/ty/typeck_results.rs3
-rw-r--r--compiler/rustc_middle/src/ty/util.rs94
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs41
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs15
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs308
-rw-r--r--compiler/rustc_mir_build/src/build/matches/simplify.rs228
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs137
-rw-r--r--compiler/rustc_mir_build/src/build/matches/util.rs52
-rw-r--r--compiler/rustc_mir_build/src/errors.rs14
-rw-r--r--compiler/rustc_mir_build/src/thir/constant.rs29
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs2
-rw-r--r--compiler/rustc_mir_transform/src/coroutine.rs6
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs180
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs15
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_drops.rs2
-rw-r--r--compiler/rustc_mir_transform/src/errors.rs3
-rw-r--r--compiler/rustc_mir_transform/src/known_panics_lint.rs (renamed from compiler/rustc_mir_transform/src/const_prop_lint.rs)32
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs4
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs1
-rw-r--r--compiler/rustc_parse/messages.ftl2
-rw-r--r--compiler/rustc_parse/src/errors.rs15
-rw-r--r--compiler/rustc_parse/src/lexer/diagnostics.rs6
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs4
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs12
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs8
-rw-r--r--compiler/rustc_parse/src/parser/item.rs29
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs9
-rw-r--r--compiler/rustc_passes/src/check_const.rs9
-rw-r--r--compiler/rustc_passes/src/dead.rs5
-rw-r--r--compiler/rustc_passes/src/errors.rs11
-rw-r--r--compiler/rustc_passes/src/lang_items.rs2
-rw-r--r--compiler/rustc_passes/src/liveness.rs4
-rw-r--r--compiler/rustc_pattern_analysis/src/errors.rs8
-rw-r--r--compiler/rustc_pattern_analysis/src/usefulness.rs164
-rw-r--r--compiler/rustc_query_system/src/dep_graph/graph.rs2
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs16
-rw-r--r--compiler/rustc_resolve/src/late.rs11
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs143
-rw-r--r--compiler/rustc_resolve/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/config.rs3
-rw-r--r--compiler/rustc_session/src/output.rs3
-rw-r--r--compiler/rustc_session/src/parse.rs13
-rw-r--r--compiler/rustc_session/src/session.rs27
-rw-r--r--compiler/rustc_smir/src/rustc_internal/mod.rs10
-rw-r--r--compiler/rustc_span/src/symbol.rs5
-rw-r--r--compiler/rustc_target/src/spec/base/apple/mod.rs107
-rw-r--r--compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs9
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs3
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs2
-rw-r--r--compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs9
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs10
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs34
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs25
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs71
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs (renamed from compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs)0
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs125
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs56
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs60
-rw-r--r--compiler/rustc_trait_selection/src/traits/normalize.rs419
-rw-r--r--compiler/rustc_trait_selection/src/traits/outlives_bounds.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs843
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs21
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs6
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs342
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs2
-rw-r--r--compiler/rustc_traits/src/normalize_projection_ty.rs58
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs18
-rw-r--r--compiler/rustc_ty_utils/src/layout.rs3
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs2
-rw-r--r--compiler/rustc_type_ir/src/flags.rs31
-rw-r--r--compiler/rustc_type_ir/src/interner.rs3
-rw-r--r--compiler/stable_mir/src/error.rs12
234 files changed, 4579 insertions, 4971 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 5cb79d9eea5..bdbc59821de 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -95,7 +95,7 @@ impl<T> ArenaChunk<T> {
         unsafe {
             if mem::size_of::<T>() == 0 {
                 // A pointer as large as possible for zero-sized elements.
-                ptr::invalid_mut(!0)
+                ptr::without_provenance_mut(!0)
             } else {
                 self.start().add(self.storage.len())
             }
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
index 274e6b7458c..834409da675 100644
--- a/compiler/rustc_ast_lowering/src/errors.rs
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -1,5 +1,6 @@
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Diagnostic, DiagnosticArgFromDisplay, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, DiagnosticArgFromDisplay, DiagnosticBuilder, EmissionGuarantee,
+    SubdiagnosticMessageOp,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
@@ -41,7 +42,11 @@ pub struct InvalidAbi {
 pub struct InvalidAbiReason(pub &'static str);
 
 impl AddToDiagnostic for InvalidAbiReason {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         #[allow(rustc::untranslatable_diagnostic)]
         diag.note(self.0);
     }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a5be91bb872..ef843da7307 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1636,9 +1636,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     if let Some(old_def_id) = self.orig_opt_local_def_id(param) {
                         old_def_id
                     } else {
-                        self.dcx()
-                            .span_delayed_bug(lifetime.ident.span, "no def-id for fresh lifetime");
-                        continue;
+                        self.dcx().span_bug(lifetime.ident.span, "no def-id for fresh lifetime");
                     }
                 }
 
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index fa0f5326196..8c9ad836087 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -881,9 +881,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
                         &item.vis,
                         errors::VisibilityNotPermittedNote::TraitImpl,
                     );
-                    // njn: use Dummy here
-                    if let TyKind::Err(_) = self_ty.kind {
-                        this.dcx().emit_err(errors::ObsoleteAuto { span: item.span });
+                    if let TyKind::Dummy = self_ty.kind {
+                        // Abort immediately otherwise the `TyKind::Dummy` will reach HIR lowering,
+                        // which isn't allowed. Not a problem for this obscure, obsolete syntax.
+                        this.dcx().emit_fatal(errors::ObsoleteAuto { span: item.span });
                     }
                     if let (&Unsafe::Yes(span), &ImplPolarity::Negative(sp)) = (unsafety, polarity)
                     {
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index 9662c73ca85..e5153c89790 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -1,7 +1,10 @@
 //! Errors emitted by ast_passes.
 
 use rustc_ast::ParamKindOrd;
-use rustc_errors::{codes::*, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessageOp};
+use rustc_errors::{
+    codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, EmissionGuarantee,
+    SubdiagnosticMessageOp,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
 
@@ -372,7 +375,11 @@ pub struct EmptyLabelManySpans(pub Vec<Span>);
 
 // The derive for `Vec<Span>` does multiple calls to `span_label`, adding commas between each
 impl AddToDiagnostic for EmptyLabelManySpans {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         diag.span_labels(self.0, "");
     }
 }
@@ -729,7 +736,11 @@ pub struct StableFeature {
 }
 
 impl AddToDiagnostic for StableFeature {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         diag.arg("name", self.name);
         diag.arg("since", self.since);
         diag.help(fluent::ast_passes_stable_since);
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 409aef9185d..1b0dd9acc37 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -22,6 +22,9 @@ macro_rules! gate {
     }};
     ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{
         if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) {
+            // FIXME: make this translatable
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            #[allow(rustc::untranslatable_diagnostic)]
             feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit();
         }
     }};
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 2b0c0e939f5..e1509da913a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -6,9 +6,7 @@
 use either::Either;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
-};
+use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
@@ -635,7 +633,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_assign_value(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         moved_place: PlaceRef<'tcx>,
         sugg_span: Span,
     ) {
@@ -674,7 +672,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_borrow_fn_like(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ty: Ty<'tcx>,
         move_sites: &[MoveSite],
         value_name: &str,
@@ -742,7 +740,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_cloning(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ty: Ty<'tcx>,
         expr: &hir::Expr<'_>,
         span: Span,
@@ -778,7 +776,12 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         }
     }
 
-    fn suggest_adding_copy_bounds(&self, err: &mut Diagnostic, ty: Ty<'tcx>, span: Span) {
+    fn suggest_adding_copy_bounds(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        ty: Ty<'tcx>,
+        span: Span,
+    ) {
         let tcx = self.infcx.tcx;
         let generics = tcx.generics_of(self.mir_def_id());
 
@@ -1225,7 +1228,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     #[instrument(level = "debug", skip(self, err))]
     fn suggest_using_local_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         location: Location,
         issued_borrow: &BorrowData<'tcx>,
         explanation: BorrowExplanation<'tcx>,
@@ -1321,7 +1324,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_slice_method_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         place: Place<'tcx>,
         borrowed_place: Place<'tcx>,
     ) {
@@ -1430,7 +1433,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// ```
     pub(crate) fn explain_iterator_advancement_in_for_loop_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         issued_spans: &UseSpans<'tcx>,
     ) {
@@ -1617,7 +1620,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// ```
     fn suggest_using_closure_argument_instead_of_capture(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         borrowed_place: Place<'tcx>,
         issued_spans: &UseSpans<'tcx>,
     ) {
@@ -1751,7 +1754,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_binding_for_closure_capture_self(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         issued_spans: &UseSpans<'tcx>,
     ) {
         let UseSpans::ClosureUse { capture_kind_span, .. } = issued_spans else { return };
@@ -2997,7 +3000,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         self.buffer_error(err);
     }
 
-    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
+    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
         let tcx = self.infcx.tcx;
         if let (
             Some(Terminator {
@@ -3532,7 +3535,11 @@ enum AnnotatedBorrowFnSignature<'tcx> {
 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
     /// Annotate the provided diagnostic with information about borrow from the fn signature that
     /// helps explain.
-    pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
+    pub(crate) fn emit(
+        &self,
+        cx: &mut MirBorrowckCtxt<'_, 'tcx>,
+        diag: &mut DiagnosticBuilder<'_>,
+    ) -> String {
         match self {
             &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
                 diag.span_label(
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index 483ecee850b..f7b59ec5fe0 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -3,7 +3,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_index::IndexSlice;
@@ -65,7 +65,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
         local_names: &IndexSlice<Local, Option<Symbol>>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         borrow_desc: &str,
         borrow_span: Option<Span>,
         multiple_borrow_span: Option<(Span, Span)>,
@@ -306,7 +306,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
     fn add_object_lifetime_default_note(
         &self,
         tcx: TyCtxt<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         unsize_ty: Ty<'tcx>,
     ) {
         if let ty::Adt(def, args) = unsize_ty.kind() {
@@ -359,7 +359,7 @@ impl<'tcx> BorrowExplanation<'tcx> {
 
     fn add_lifetime_bound_suggestion_to_diagnostic(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         category: &ConstraintCategory<'tcx>,
         span: Span,
         region_name: &RegionName,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 4ca854c857d..db0d69b6eaa 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -5,7 +5,7 @@ use crate::session_diagnostics::{
     CaptureVarKind, CaptureVarPathUseCause, OnClosureNote,
 };
 use itertools::Itertools;
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, Namespace};
 use rustc_hir::CoroutineKind;
@@ -80,7 +80,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         location: Location,
         place: PlaceRef<'tcx>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
         let mut target = place.local_or_deref_local();
@@ -588,7 +588,7 @@ impl UseSpans<'_> {
     pub(super) fn args_subdiag(
         self,
         dcx: &rustc_errors::DiagCtxt,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         f: impl FnOnce(Span) -> CaptureArgLabel,
     ) {
         if let UseSpans::ClosureUse { args_span, .. } = self {
@@ -601,7 +601,7 @@ impl UseSpans<'_> {
     pub(super) fn var_path_only_subdiag(
         self,
         dcx: &rustc_errors::DiagCtxt,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         action: crate::InitializationRequiringAction,
     ) {
         use crate::InitializationRequiringAction::*;
@@ -638,7 +638,7 @@ impl UseSpans<'_> {
     pub(super) fn var_subdiag(
         self,
         dcx: &rustc_errors::DiagCtxt,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         kind: Option<rustc_middle::mir::BorrowKind>,
         f: impl FnOnce(hir::ClosureKind, Span) -> CaptureVarCause,
     ) {
@@ -1010,7 +1010,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn explain_captures(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         move_span: Span,
         move_spans: UseSpans<'tcx>,
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index dad20690d02..3478a73254a 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -1,7 +1,7 @@
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_middle::mir::*;
 use rustc_middle::ty::{self, Ty};
 use rustc_mir_dataflow::move_paths::{LookupResult, MovePathIndex};
@@ -437,7 +437,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         err
     }
 
-    fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diagnostic, span: Span) {
+    fn add_move_hints(
+        &self,
+        error: GroupedMoveError<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+        span: Span,
+    ) {
         match error {
             GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
                 self.add_borrow_suggestions(err, span);
@@ -500,7 +505,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn add_borrow_suggestions(&self, err: &mut Diagnostic, span: Span) {
+    fn add_borrow_suggestions(&self, err: &mut DiagnosticBuilder<'_>, span: Span) {
         match self.infcx.tcx.sess.source_map().span_to_snippet(span) {
             Ok(snippet) if snippet.starts_with('*') => {
                 err.span_suggestion_verbose(
@@ -521,7 +526,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn add_move_error_suggestions(&self, err: &mut Diagnostic, binds_to: &[Local]) {
+    fn add_move_error_suggestions(&self, err: &mut DiagnosticBuilder<'_>, binds_to: &[Local]) {
         let mut suggestions: Vec<(Span, String, String)> = Vec::new();
         for local in binds_to {
             let bind_to = &self.body.local_decls[*local];
@@ -573,7 +578,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn add_move_error_details(&self, err: &mut Diagnostic, binds_to: &[Local]) {
+    fn add_move_error_details(&self, err: &mut DiagnosticBuilder<'_>, binds_to: &[Local]) {
         for (j, local) in binds_to.iter().enumerate() {
             let bind_to = &self.body.local_decls[*local];
             let binding_span = bind_to.source_info.span;
@@ -610,7 +615,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// expansion of a packed struct.
     /// Such errors happen because derive macro expansions shy away from taking
     /// references to the struct's fields since doing so would be undefined behaviour
-    fn add_note_for_packed_struct_derive(&self, err: &mut Diagnostic, local: Local) {
+    fn add_note_for_packed_struct_derive(&self, err: &mut DiagnosticBuilder<'_>, local: Local) {
         let local_place: PlaceRef<'tcx> = local.into();
         let local_ty = local_place.ty(self.body.local_decls(), self.infcx.tcx).ty.peel_refs();
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 514e9c39eb4..b8257ba0adc 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -2,7 +2,7 @@
 #![allow(rustc::untranslatable_diagnostic)]
 
 use hir::ExprKind;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::Node;
@@ -540,7 +540,12 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) {
+    fn suggest_map_index_mut_alternatives(
+        &self,
+        ty: Ty<'tcx>,
+        err: &mut DiagnosticBuilder<'tcx>,
+        span: Span,
+    ) {
         let Some(adt) = ty.ty_adt_def() else { return };
         let did = adt.did();
         if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
@@ -548,7 +553,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         {
             struct V<'a, 'tcx> {
                 assign_span: Span,
-                err: &'a mut Diagnostic,
+                err: &'a mut DiagnosticBuilder<'tcx>,
                 ty: Ty<'tcx>,
                 suggested: bool,
             }
@@ -790,7 +795,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         tcx: TyCtxt<'_>,
         closure_local_def_id: hir::def_id::LocalDefId,
         the_place_err: PlaceRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         let tables = tcx.typeck(closure_local_def_id);
         if let Some((span, closure_kind_origin)) = tcx.closure_kind_origin(closure_local_def_id) {
@@ -852,7 +857,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     // Attempt to search similar mutable associated items for suggestion.
     // In the future, attempt in all path but initially for RHS of for_loop
-    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic, span: Span) {
+    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>, span: Span) {
         use hir::{
             BorrowKind, Expr,
             ExprKind::{AddrOf, Block, Call, MethodCall},
@@ -936,7 +941,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     }
 
     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
-    fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
+    fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
         err.span_label(sp, format!("cannot {act}"));
 
         let hir = self.infcx.tcx.hir();
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index 93e28a5f3f3..b2c7a98142e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -5,7 +5,7 @@
 #![allow(rustc::untranslatable_diagnostic)]
 
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_middle::ty::RegionVid;
 use smallvec::SmallVec;
 use std::collections::BTreeMap;
@@ -158,13 +158,13 @@ impl OutlivesSuggestionBuilder {
         self.constraints_to_add.entry(fr).or_default().push(outlived_fr);
     }
 
-    /// Emit an intermediate note on the given `Diagnostic` if the involved regions are
+    /// Emit an intermediate note on the given `DiagnosticBuilder` if the involved regions are
     /// suggestable.
     pub(crate) fn intermediate_suggestion(
         &mut self,
         mbcx: &MirBorrowckCtxt<'_, '_>,
         errci: &ErrorConstraintInfo<'_>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         // Emit an intermediate note.
         let fr_name = self.region_vid_to_name(mbcx, errci.fr);
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index e8effd5c163..50d22881c3e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1,7 +1,7 @@
 //! Error reporting machinery for lifetime errors.
 
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::Res::Def;
 use rustc_hir::def_id::DefId;
@@ -251,6 +251,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
         hrtb_bounds.iter().for_each(|bound| {
             let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; };
+            // FIXME: make this translatable
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            #[allow(rustc::untranslatable_diagnostic)]
             diag.span_note(
                 *trait_span,
                 "due to current limitations in the borrow checker, this implies a `'static` lifetime"
@@ -421,6 +424,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// ```
     ///
     /// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`.
+    // FIXME: make this translatable
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     pub(crate) fn report_region_error(
         &mut self,
         fr: RegionVid,
@@ -685,12 +691,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
             borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from);
 
         if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span {
+            // FIXME: make this translatable
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            #[allow(rustc::untranslatable_diagnostic)]
             diag.span_label(
                 outlived_fr_span,
                 format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",),
             );
         }
 
+        // FIXME: make this translatable
+        #[allow(rustc::diagnostic_outside_of_impl)]
+        #[allow(rustc::untranslatable_diagnostic)]
         if let Some((Some(fr_name), fr_span)) = fr_name_and_span {
             diag.span_label(
                 fr_span,
@@ -714,6 +726,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap();
                 outlived_fr_region_name.highlight_region_name(&mut diag);
 
+                // FIXME: make this translatable
+                #[allow(rustc::diagnostic_outside_of_impl)]
+                #[allow(rustc::untranslatable_diagnostic)]
                 diag.span_label(
                     *span,
                     format!(
@@ -808,7 +823,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// ```
     fn add_static_impl_trait_suggestion(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         fr: RegionVid,
         // We need to pass `fr_name` - computing it again will label it twice.
         fr_name: RegionName,
@@ -897,7 +912,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     fn maybe_suggest_constrain_dyn_trait_impl(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         f: Region<'tcx>,
         o: Region<'tcx>,
         category: &ConstraintCategory<'tcx>,
@@ -959,7 +974,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     #[instrument(skip(self, err), level = "debug")]
     fn suggest_constrain_dyn_trait_in_impl(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         found_dids: &FxIndexSet<DefId>,
         ident: Ident,
         self_ty: &hir::Ty<'_>,
@@ -994,7 +1009,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     fn suggest_adding_lifetime_params(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         sub: RegionVid,
         sup: RegionVid,
     ) {
@@ -1023,7 +1038,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
     }
 
-    fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
+    fn suggest_move_on_borrowing_closure(&self, diag: &mut DiagnosticBuilder<'_>) {
         let map = self.infcx.tcx.hir();
         let body_id = map.body_owned_by(self.mir_def_id());
         let expr = &map.body(body_id).value.peel_blocks();
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index e008d230656..e228bef1139 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -5,7 +5,7 @@ use std::fmt::{self, Display};
 use std::iter;
 
 use rustc_data_structures::fx::IndexEntry;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_middle::ty::print::RegionHighlightMode;
@@ -106,7 +106,7 @@ impl RegionName {
         }
     }
 
-    pub(crate) fn highlight_region_name(&self, diag: &mut Diagnostic) {
+    pub(crate) fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) {
         match &self.source {
             RegionNameSource::NamedLateParamRegion(span)
             | RegionNameSource::NamedEarlyParamRegion(span) => {
@@ -626,9 +626,9 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                     | GenericArgKind::Const(_),
                     _,
                 ) => {
-                    // HIR lowering sometimes doesn't catch this in erroneous
-                    // programs, so we need to use span_delayed_bug here. See #82126.
-                    self.dcx().span_delayed_bug(
+                    // This was previously a `span_delayed_bug` and could be
+                    // reached by the test for #82126, but no longer.
+                    self.dcx().span_bug(
                         hir_arg.span(),
                         format!("unmatched arg and hir arg: found {kind:?} vs {hir_arg:?}"),
                     );
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 6c2a511538d..dbaa9e5bcfa 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2497,6 +2497,8 @@ mod diags {
             }
             for (_, (mut diag, count)) in std::mem::take(&mut self.diags.buffered_mut_errors) {
                 if count > 10 {
+                    #[allow(rustc::diagnostic_outside_of_impl)]
+                    #[allow(rustc::untranslatable_diagnostic)]
                     diag.note(format!("...and {} other attempted mutable borrows", count - 10));
                 }
                 self.diags.buffered_diags.push(BufferedDiag::Error(diag));
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index 34d60fc8f6e..ce2c0dbaff7 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -5,7 +5,7 @@ use rustc_data_structures::binary_search_util;
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::graph::scc::Sccs;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir::def_id::CRATE_DEF_ID;
 use rustc_index::{IndexSlice, IndexVec};
 use rustc_infer::infer::outlives::test_type_match;
@@ -592,7 +592,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     }
 
     /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`.
-    pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
+    pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_, ()>) {
         self.universal_regions.annotate(tcx, err)
     }
 
diff --git a/compiler/rustc_borrowck/src/type_check/canonical.rs b/compiler/rustc_borrowck/src/type_check/canonical.rs
index fc600af1b76..e5ebf97cfc4 100644
--- a/compiler/rustc_borrowck/src/type_check/canonical.rs
+++ b/compiler/rustc_borrowck/src/type_check/canonical.rs
@@ -100,7 +100,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
     ) {
         for (predicate, span) in instantiated_predicates {
-            debug!(?predicate);
+            debug!(?span, ?predicate);
             let category = ConstraintCategory::Predicate(span);
             let predicate = self.normalize_with_category(predicate, locations, category);
             self.prove_predicate(predicate, locations, category);
diff --git a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
index c97e3170166..de75a9857f8 100644
--- a/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
+++ b/compiler/rustc_borrowck/src/type_check/constraint_conversion.rs
@@ -8,7 +8,7 @@ use rustc_middle::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Const
 use rustc_middle::traits::query::NoSolution;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_trait_selection::solve::deeply_normalize;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
 use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -183,7 +183,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
 
                         // we don't actually use this for anything, but
                         // the `TypeOutlives` code needs an origin.
-                        let origin = infer::RelateParamBound(DUMMY_SP, t1, None);
+                        let origin = infer::RelateParamBound(self.span, t1, None);
 
                         TypeOutlives::new(
                             &mut *self,
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 2e0caf44819..897918fb0a4 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -10,7 +10,7 @@ use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::query::OutlivesBound;
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{self, RegionVid, Ty, TypeVisitableExt};
-use rustc_span::{ErrorGuaranteed, DUMMY_SP};
+use rustc_span::{ErrorGuaranteed, Span};
 use rustc_trait_selection::solve::deeply_normalize;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
@@ -269,7 +269,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
             debug!("build: input_or_output={:?}", ty);
             // We add implied bounds from both the unnormalized and normalized ty.
             // See issue #87748
-            let constraints_unnorm = self.add_implied_bounds(ty);
+            let constraints_unnorm = self.add_implied_bounds(ty, span);
             if let Some(c) = constraints_unnorm {
                 constraints.push(c)
             }
@@ -299,7 +299,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
             // ```
             // Both &Self::Bar and &() are WF
             if ty != norm_ty {
-                let constraints_norm = self.add_implied_bounds(norm_ty);
+                let constraints_norm = self.add_implied_bounds(norm_ty, span);
                 if let Some(c) = constraints_norm {
                     constraints.push(c)
                 }
@@ -316,6 +316,9 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                     .and(type_op::normalize::Normalize::new(ty))
                     .fully_perform(self.infcx, span)
                 else {
+                    // Note: this path is currently not reached in any test, so
+                    // any example that triggers this would be worth minimizing
+                    // and converting into a test.
                     tcx.dcx().span_delayed_bug(span, format!("failed to normalize {ty:?}"));
                     continue;
                 };
@@ -323,7 +326,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
 
                 // We currently add implied bounds from the normalized ty only.
                 // This is more conservative and matches wfcheck behavior.
-                let c = self.add_implied_bounds(norm_ty);
+                let c = self.add_implied_bounds(norm_ty, span);
                 constraints.extend(c);
             }
         }
@@ -361,11 +364,15 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
     /// the same time, compute and add any implied bounds that come
     /// from this local.
     #[instrument(level = "debug", skip(self))]
-    fn add_implied_bounds(&mut self, ty: Ty<'tcx>) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
+    fn add_implied_bounds(
+        &mut self,
+        ty: Ty<'tcx>,
+        span: Span,
+    ) -> Option<&'tcx QueryRegionConstraints<'tcx>> {
         let TypeOpOutput { output: bounds, constraints, .. } = self
             .param_env
             .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
-            .fully_perform(self.infcx, DUMMY_SP)
+            .fully_perform(self.infcx, span)
             .map_err(|_: ErrorGuaranteed| debug!("failed to compute implied bounds {:?}", ty))
             .ok()?;
         debug!(?bounds, ?constraints);
diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index af5b635ae66..8af78b08f69 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -154,8 +154,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
             if argument_index + 1 >= body.local_decls.len() {
                 self.tcx()
                     .dcx()
-                    .span_delayed_bug(body.span, "found more normalized_input_ty than local_decls");
-                break;
+                    .span_bug(body.span, "found more normalized_input_ty than local_decls");
             }
 
             // In MIR, argument N is stored in local N+1.
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 64469727d0d..81193902f9b 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -37,7 +37,7 @@ use rustc_mir_dataflow::points::DenseLocationMap;
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::sym;
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::Span;
 use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
 use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
 use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
@@ -220,14 +220,13 @@ pub(crate) fn type_check<'mir, 'tcx>(
                     "opaque_type_map",
                 ),
             );
-            let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
+            let hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
             trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
             if hidden_type.has_non_region_infer() {
-                let reported = infcx.dcx().span_delayed_bug(
+                infcx.dcx().span_bug(
                     decl.hidden_type.span,
                     format!("could not resolve {:#?}", hidden_type.ty.kind()),
                 );
-                hidden_type.ty = Ty::new_error(infcx.tcx, reported);
             }
 
             (opaque_type_key, hidden_type)
@@ -1014,7 +1013,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     ) -> Self {
         let mut checker = Self {
             infcx,
-            last_span: DUMMY_SP,
+            last_span: body.span,
             body,
             user_type_annotations: &body.user_type_annotations,
             param_env,
@@ -1089,10 +1088,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         );
 
         if result.is_err() {
-            self.infcx.dcx().span_delayed_bug(
-                self.body.span,
-                "failed re-defining predefined opaques in mir typeck",
-            );
+            self.infcx
+                .dcx()
+                .span_bug(self.body.span, "failed re-defining predefined opaques in mir typeck");
         }
     }
 
@@ -2766,7 +2764,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 self.param_env,
                 self.known_type_outlives_obligations,
                 locations,
-                DUMMY_SP,                   // irrelevant; will be overridden.
+                self.body.span,             // irrelevant; will be overridden.
                 ConstraintCategory::Boring, // same as above.
                 self.borrowck_context.constraints,
             )
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
index ee0bd13109b..dd355c3525c 100644
--- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs
+++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs
@@ -1,11 +1,14 @@
+use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::ErrorGuaranteed;
-use rustc_infer::infer::nll_relate::{TypeRelating, TypeRelatingDelegate};
-use rustc_infer::infer::NllRegionVariableOrigin;
-use rustc_infer::traits::PredicateObligations;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{NllRegionVariableOrigin, ObligationEmittingRelation};
+use rustc_infer::traits::{Obligation, PredicateObligations};
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::traits::query::NoSolution;
-use rustc_middle::ty::relate::TypeRelation;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::traits::ObligationCause;
+use rustc_middle::ty::fold::FnMutDelegate;
+use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
 
@@ -32,12 +35,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) -> Result<(), NoSolution> {
-        TypeRelating::new(
-            self.infcx,
-            NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::relate(a, b)),
-            v,
-        )
-        .relate(a, b)?;
+        NllTypeRelating::new(self, locations, category, UniverseInfo::relate(a, b), v)
+            .relate(a, b)?;
         Ok(())
     }
 
@@ -49,9 +48,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         locations: Locations,
         category: ConstraintCategory<'tcx>,
     ) -> Result<(), NoSolution> {
-        TypeRelating::new(
-            self.infcx,
-            NllTypeRelatingDelegate::new(self, locations, category, UniverseInfo::other()),
+        NllTypeRelating::new(
+            self,
+            locations,
+            category,
+            UniverseInfo::other(),
             ty::Variance::Invariant,
         )
         .relate(a, b)?;
@@ -59,7 +60,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
     }
 }
 
-struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {
+pub struct NllTypeRelating<'me, 'bccx, 'tcx> {
     type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
 
     /// Where (and why) is this relation taking place?
@@ -71,26 +72,177 @@ struct NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {
     /// Information so that error reporting knows what types we are relating
     /// when reporting a bound region error.
     universe_info: UniverseInfo<'tcx>,
+
+    /// How are we relating `a` and `b`?
+    ///
+    /// - Covariant means `a <: b`.
+    /// - Contravariant means `b <: a`.
+    /// - Invariant means `a == b`.
+    /// - Bivariant means that it doesn't matter.
+    ambient_variance: ty::Variance,
+
+    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
 }
 
-impl<'me, 'bccx, 'tcx> NllTypeRelatingDelegate<'me, 'bccx, 'tcx> {
-    fn new(
+impl<'me, 'bccx, 'tcx> NllTypeRelating<'me, 'bccx, 'tcx> {
+    pub fn new(
         type_checker: &'me mut TypeChecker<'bccx, 'tcx>,
         locations: Locations,
         category: ConstraintCategory<'tcx>,
         universe_info: UniverseInfo<'tcx>,
+        ambient_variance: ty::Variance,
     ) -> Self {
-        Self { type_checker, locations, category, universe_info }
+        Self {
+            type_checker,
+            locations,
+            category,
+            universe_info,
+            ambient_variance,
+            ambient_variance_info: ty::VarianceDiagInfo::default(),
+        }
     }
-}
 
-impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx> {
-    fn span(&self) -> Span {
-        self.locations.span(self.type_checker.body)
+    fn ambient_covariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Covariant | ty::Variance::Invariant => true,
+            ty::Variance::Contravariant | ty::Variance::Bivariant => false,
+        }
     }
 
-    fn param_env(&self) -> ty::ParamEnv<'tcx> {
-        self.type_checker.param_env
+    fn ambient_contravariance(&self) -> bool {
+        match self.ambient_variance {
+            ty::Variance::Contravariant | ty::Variance::Invariant => true,
+            ty::Variance::Covariant | ty::Variance::Bivariant => false,
+        }
+    }
+
+    fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
+        let infcx = self.type_checker.infcx;
+        debug_assert!(!infcx.next_trait_solver());
+        let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
+        // `handle_opaque_type` cannot handle subtyping, so to support subtyping
+        // we instead eagerly generalize here. This is a bit of a mess but will go
+        // away once we're using the new solver.
+        let mut enable_subtyping = |ty, ty_is_expected| {
+            let ty_vid = infcx.next_ty_var_id_in_universe(
+                TypeVariableOrigin {
+                    kind: TypeVariableOriginKind::MiscVariable,
+                    span: self.span(),
+                },
+                ty::UniverseIndex::ROOT,
+            );
+
+            let variance = if ty_is_expected {
+                self.ambient_variance
+            } else {
+                self.ambient_variance.xform(ty::Contravariant)
+            };
+
+            self.type_checker.infcx.instantiate_ty_var(
+                self,
+                ty_is_expected,
+                ty_vid,
+                variance,
+                ty,
+            )?;
+            Ok(infcx.resolve_vars_if_possible(Ty::new_infer(infcx.tcx, ty::TyVar(ty_vid))))
+        };
+
+        let (a, b) = match (a.kind(), b.kind()) {
+            (&ty::Alias(ty::Opaque, ..), _) => (a, enable_subtyping(b, false)?),
+            (_, &ty::Alias(ty::Opaque, ..)) => (enable_subtyping(a, true)?, b),
+            _ => unreachable!(
+                "expected at least one opaque type in `relate_opaques`, got {a} and {b}."
+            ),
+        };
+        let cause = ObligationCause::dummy_with_span(self.span());
+        let obligations =
+            infcx.handle_opaque_type(a, b, true, &cause, self.param_env())?.obligations;
+        self.register_obligations(obligations);
+        Ok(())
+    }
+
+    fn enter_forall<T, U>(
+        &mut self,
+        binder: ty::Binder<'tcx, T>,
+        f: impl FnOnce(&mut Self, T) -> U,
+    ) -> U
+    where
+        T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
+    {
+        let value = if let Some(inner) = binder.no_bound_vars() {
+            inner
+        } else {
+            let infcx = self.type_checker.infcx;
+            let mut lazy_universe = None;
+            let delegate = FnMutDelegate {
+                regions: &mut |br: ty::BoundRegion| {
+                    // The first time this closure is called, create a
+                    // new universe for the placeholders we will make
+                    // from here out.
+                    let universe = lazy_universe.unwrap_or_else(|| {
+                        let universe = self.create_next_universe();
+                        lazy_universe = Some(universe);
+                        universe
+                    });
+
+                    let placeholder = ty::PlaceholderRegion { universe, bound: br };
+                    debug!(?placeholder);
+                    let placeholder_reg = self.next_placeholder_region(placeholder);
+                    debug!(?placeholder_reg);
+
+                    placeholder_reg
+                },
+                types: &mut |_bound_ty: ty::BoundTy| {
+                    unreachable!("we only replace regions in nll_relate, not types")
+                },
+                consts: &mut |_bound_var: ty::BoundVar, _ty| {
+                    unreachable!("we only replace regions in nll_relate, not consts")
+                },
+            };
+
+            infcx.tcx.replace_bound_vars_uncached(binder, delegate)
+        };
+
+        debug!(?value);
+        f(self, value)
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn instantiate_binder_with_existentials<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T
+    where
+        T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
+    {
+        if let Some(inner) = binder.no_bound_vars() {
+            return inner;
+        }
+
+        let infcx = self.type_checker.infcx;
+        let mut reg_map = FxHashMap::default();
+        let delegate = FnMutDelegate {
+            regions: &mut |br: ty::BoundRegion| {
+                if let Some(ex_reg_var) = reg_map.get(&br) {
+                    return *ex_reg_var;
+                } else {
+                    let ex_reg_var = self.next_existential_region_var(true, br.kind.get_name());
+                    debug!(?ex_reg_var);
+                    reg_map.insert(br, ex_reg_var);
+
+                    ex_reg_var
+                }
+            },
+            types: &mut |_bound_ty: ty::BoundTy| {
+                unreachable!("we only replace regions in nll_relate, not types")
+            },
+            consts: &mut |_bound_var: ty::BoundVar, _ty| {
+                unreachable!("we only replace regions in nll_relate, not consts")
+            },
+        };
+
+        let replaced = infcx.tcx.replace_bound_vars_uncached(binder, delegate);
+        debug!(?replaced);
+
+        replaced
     }
 
     fn create_next_universe(&mut self) -> ty::UniverseIndex {
@@ -143,22 +295,6 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
         reg
     }
 
-    #[instrument(skip(self), level = "debug")]
-    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
-        let reg = self.type_checker.infcx.next_nll_region_var_in_universe(
-            NllRegionVariableOrigin::Existential { from_forall: false },
-            universe,
-        );
-
-        if cfg!(debug_assertions) {
-            let mut var_to_origin = self.type_checker.infcx.reg_var_to_origin.borrow_mut();
-            let prev = var_to_origin.insert(reg.as_var(), RegionCtxt::Existential(None));
-            assert_eq!(prev, None);
-        }
-
-        reg
-    }
-
     fn push_outlives(
         &mut self,
         sup: ty::Region<'tcx>,
@@ -179,11 +315,250 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
             },
         );
     }
+}
+
+impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.type_checker.infcx.tcx
+    }
+
+    fn tag(&self) -> &'static str {
+        "nll::subtype"
+    }
 
-    fn forbid_inference_vars() -> bool {
+    fn a_is_expected(&self) -> bool {
         true
     }
 
+    #[instrument(skip(self, info), level = "trace", ret)]
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        variance: ty::Variance,
+        info: ty::VarianceDiagInfo<'tcx>,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        let old_ambient_variance = self.ambient_variance;
+        self.ambient_variance = self.ambient_variance.xform(variance);
+        self.ambient_variance_info = self.ambient_variance_info.xform(info);
+
+        debug!(?self.ambient_variance);
+        // In a bivariant context this always succeeds.
+        let r =
+            if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? };
+
+        self.ambient_variance = old_ambient_variance;
+
+        Ok(r)
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        let infcx = self.type_checker.infcx;
+
+        let a = self.type_checker.infcx.shallow_resolve(a);
+        assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
+
+        if a == b {
+            return Ok(a);
+        }
+
+        match (a.kind(), b.kind()) {
+            (_, &ty::Infer(ty::TyVar(_))) => {
+                span_bug!(
+                    self.span(),
+                    "should not be relating type variables on the right in MIR typeck"
+                );
+            }
+
+            (&ty::Infer(ty::TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var(self, true, a_vid, self.ambient_variance, b)?
+            }
+
+            (
+                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
+                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
+            ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
+                infcx.super_combine_tys(self, a, b).map(|_| ()).or_else(|err| {
+                    // This behavior is only there for the old solver, the new solver
+                    // shouldn't ever fail. Instead, it unconditionally emits an
+                    // alias-relate goal.
+                    assert!(!self.type_checker.infcx.next_trait_solver());
+                    self.tcx().dcx().span_delayed_bug(
+                        self.span(),
+                        "failure to relate an opaque to itself should result in an error later on",
+                    );
+                    if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
+                })?;
+            }
+            (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
+            | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
+                if def_id.is_local() && !self.type_checker.infcx.next_trait_solver() =>
+            {
+                self.relate_opaques(a, b)?;
+            }
+
+            _ => {
+                debug!(?a, ?b, ?self.ambient_variance);
+
+                // Will also handle unification of `IntVar` and `FloatVar`.
+                self.type_checker.infcx.super_combine_tys(self, a, b)?;
+            }
+        }
+
+        Ok(a)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn regions(
+        &mut self,
+        a: ty::Region<'tcx>,
+        b: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug!(?self.ambient_variance);
+
+        if self.ambient_covariance() {
+            // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`.
+            self.push_outlives(a, b, self.ambient_variance_info);
+        }
+
+        if self.ambient_contravariance() {
+            // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`.
+            self.push_outlives(b, a, self.ambient_variance_info);
+        }
+
+        Ok(a)
+    }
+
+    fn consts(
+        &mut self,
+        a: ty::Const<'tcx>,
+        b: ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+        let a = self.type_checker.infcx.shallow_resolve(a);
+        assert!(!a.has_non_region_infer(), "unexpected inference var {:?}", a);
+        assert!(!b.has_non_region_infer(), "unexpected inference var {:?}", b);
+
+        self.type_checker.infcx.super_combine_consts(self, a, b)
+    }
+
+    #[instrument(skip(self), level = "trace")]
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<'tcx, T>,
+        b: ty::Binder<'tcx, T>,
+    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
+    where
+        T: Relate<'tcx>,
+    {
+        // We want that
+        //
+        // ```
+        // for<'a> fn(&'a u32) -> &'a u32 <:
+        //   fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // but not
+        //
+        // ```
+        // fn(&'a u32) -> &'a u32 <:
+        //   for<'b> fn(&'b u32) -> &'b u32
+        // ```
+        //
+        // We therefore proceed as follows:
+        //
+        // - Instantiate binders on `b` universally, yielding a universe U1.
+        // - Instantiate binders on `a` existentially in U1.
+
+        debug!(?self.ambient_variance);
+
+        if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
+            // Fast path for the common case.
+            self.relate(a, b)?;
+            return Ok(ty::Binder::dummy(a));
+        }
+
+        if self.ambient_covariance() {
+            // Covariance, so we want `for<..> A <: for<..> B` --
+            // therefore we compare any instantiation of A (i.e., A
+            // instantiated with existentials) against every
+            // instantiation of B (i.e., B instantiated with
+            // universals).
+
+            // Reset the ambient variance to covariant. This is needed
+            // to correctly handle cases like
+            //
+            //     for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32)
+            //
+            // Somewhat surprisingly, these two types are actually
+            // **equal**, even though the one on the right looks more
+            // polymorphic. The reason is due to subtyping. To see it,
+            // consider that each function can call the other:
+            //
+            // - The left function can call the right with `'b` and
+            //   `'c` both equal to `'a`
+            //
+            // - The right function can call the left with `'a` set to
+            //   `{P}`, where P is the point in the CFG where the call
+            //   itself occurs. Note that `'b` and `'c` must both
+            //   include P. At the point, the call works because of
+            //   subtyping (i.e., `&'b u32 <: &{P} u32`).
+            let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant);
+
+            // Note: the order here is important. Create the placeholders first, otherwise
+            // we assign the wrong universe to the existential!
+            self.enter_forall(b, |this, b| {
+                let a = this.instantiate_binder_with_existentials(a);
+                this.relate(a, b)
+            })?;
+
+            self.ambient_variance = variance;
+        }
+
+        if self.ambient_contravariance() {
+            // Contravariance, so we want `for<..> A :> for<..> B`
+            // -- therefore we compare every instantiation of A (i.e.,
+            // A instantiated with universals) against any
+            // instantiation of B (i.e., B instantiated with
+            // existentials). Opposite of above.
+
+            // Reset ambient variance to contravariance. See the
+            // covariant case above for an explanation.
+            let variance =
+                std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
+
+            self.enter_forall(a, |this, a| {
+                let b = this.instantiate_binder_with_existentials(b);
+                this.relate(a, b)
+            })?;
+
+            self.ambient_variance = variance;
+        }
+
+        Ok(a)
+    }
+}
+
+impl<'bccx, 'tcx> ObligationEmittingRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
+    fn span(&self) -> Span {
+        self.locations.span(self.type_checker.body)
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.type_checker.param_env
+    }
+
+    fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
+        self.register_obligations(
+            obligations
+                .into_iter()
+                .map(|to_pred| {
+                    Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred)
+                })
+                .collect(),
+        );
+    }
+
     fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
         let _: Result<_, ErrorGuaranteed> = self.type_checker.fully_perform_op(
             self.locations,
@@ -196,4 +571,32 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for NllTypeRelatingDelegate<'_, '_, 'tcx>
             },
         );
     }
+
+    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
+        unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance")
+    }
+
+    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
+        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
+            ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            // a :> b is b <: a
+            ty::Variance::Contravariant => ty::PredicateKind::AliasRelate(
+                b.into(),
+                a.into(),
+                ty::AliasRelationDirection::Subtype,
+            ),
+            ty::Variance::Invariant => ty::PredicateKind::AliasRelate(
+                a.into(),
+                b.into(),
+                ty::AliasRelationDirection::Equate,
+            ),
+            ty::Variance::Bivariant => {
+                unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)")
+            }
+        })]);
+    }
 }
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index a69f5335f71..e7439481034 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -16,7 +16,7 @@
 #![allow(rustc::untranslatable_diagnostic)]
 
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::BodyOwnerKind;
@@ -343,7 +343,7 @@ impl<'tcx> UniversalRegions<'tcx> {
     /// that this region imposes on others. The methods in this file
     /// handle the part about dumping the inference context internal
     /// state.
-    pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
+    pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_, ()>) {
         match self.defining_ty {
             DefiningTy::Closure(def_id, args) => {
                 let v = with_no_trimmed_paths!(
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index 0b2e63b403b..c5a73c31995 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -773,7 +773,7 @@ pub(super) fn expand_global_asm<'cx>(
                         kind: ast::VisibilityKind::Inherited,
                         tokens: None,
                     },
-                    span: ecx.with_def_site_ctxt(sp),
+                    span: sp,
                     tokens: None,
                 })])
             } else {
diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs
index 8d2e06bf30d..f304a37be85 100644
--- a/compiler/rustc_builtin_macros/src/errors.rs
+++ b/compiler/rustc_builtin_macros/src/errors.rs
@@ -1,6 +1,6 @@
 use rustc_errors::{
-    codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee,
-    IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic,
+    Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_span::{symbol::Ident, Span, Symbol};
@@ -611,7 +611,11 @@ pub(crate) struct FormatUnusedArg {
 // Allow the singular form to be a subdiagnostic of the multiple-unused
 // form of diagnostic.
 impl AddToDiagnostic for FormatUnusedArg {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         diag.arg("named", self.named);
         let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into());
         diag.span_label(self.span, msg);
diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
index 476752c7230..199d5df29e7 100644
--- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
+++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs
@@ -1152,17 +1152,26 @@ fn codegen_regular_intrinsic_call<'tcx>(
             ret.write_cvalue(fx, ret_val);
         }
 
-        sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
+        sym::fadd_fast
+        | sym::fsub_fast
+        | sym::fmul_fast
+        | sym::fdiv_fast
+        | sym::frem_fast
+        | sym::fadd_algebraic
+        | sym::fsub_algebraic
+        | sym::fmul_algebraic
+        | sym::fdiv_algebraic
+        | sym::frem_algebraic => {
             intrinsic_args!(fx, args => (x, y); intrinsic);
 
             let res = crate::num::codegen_float_binop(
                 fx,
                 match intrinsic {
-                    sym::fadd_fast => BinOp::Add,
-                    sym::fsub_fast => BinOp::Sub,
-                    sym::fmul_fast => BinOp::Mul,
-                    sym::fdiv_fast => BinOp::Div,
-                    sym::frem_fast => BinOp::Rem,
+                    sym::fadd_fast | sym::fadd_algebraic => BinOp::Add,
+                    sym::fsub_fast | sym::fsub_algebraic => BinOp::Sub,
+                    sym::fmul_fast | sym::fmul_algebraic => BinOp::Mul,
+                    sym::fdiv_fast | sym::fdiv_algebraic => BinOp::Div,
+                    sym::frem_fast | sym::frem_algebraic => BinOp::Rem,
                     _ => unreachable!(),
                 },
                 x,
diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs
index 42e61b3ccb5..5f1e4538376 100644
--- a/compiler/rustc_codegen_gcc/src/builder.rs
+++ b/compiler/rustc_codegen_gcc/src/builder.rs
@@ -705,6 +705,31 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
         self.frem(lhs, rhs)
     }
 
+    fn fadd_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
+        lhs + rhs
+    }
+
+    fn fsub_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
+        lhs - rhs
+    }
+
+    fn fmul_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
+        lhs * rhs
+    }
+
+    fn fdiv_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
+        lhs / rhs
+    }
+
+    fn frem_algebraic(&mut self, lhs: RValue<'gcc>, rhs: RValue<'gcc>) -> RValue<'gcc> {
+        // NOTE: it seems like we cannot enable fast-mode for a single operation in GCC.
+        self.frem(lhs, rhs)
+    }
+
     fn checked_binop(&mut self, oop: OverflowOp, typ: Ty<'_>, lhs: Self::Value, rhs: Self::Value) -> (Self::Value, Self::Value) {
         self.gcc_checked_binop(oop, typ, lhs, rhs)
     }
diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs
index 7ed27b33dce..cfa266720d2 100644
--- a/compiler/rustc_codegen_llvm/src/builder.rs
+++ b/compiler/rustc_codegen_llvm/src/builder.rs
@@ -340,6 +340,46 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
         }
     }
 
+    fn fadd_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
+        unsafe {
+            let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, UNNAMED);
+            llvm::LLVMRustSetAlgebraicMath(instr);
+            instr
+        }
+    }
+
+    fn fsub_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
+        unsafe {
+            let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, UNNAMED);
+            llvm::LLVMRustSetAlgebraicMath(instr);
+            instr
+        }
+    }
+
+    fn fmul_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
+        unsafe {
+            let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, UNNAMED);
+            llvm::LLVMRustSetAlgebraicMath(instr);
+            instr
+        }
+    }
+
+    fn fdiv_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
+        unsafe {
+            let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, UNNAMED);
+            llvm::LLVMRustSetAlgebraicMath(instr);
+            instr
+        }
+    }
+
+    fn frem_algebraic(&mut self, lhs: &'ll Value, rhs: &'ll Value) -> &'ll Value {
+        unsafe {
+            let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, UNNAMED);
+            llvm::LLVMRustSetAlgebraicMath(instr);
+            instr
+        }
+    }
+
     fn checked_binop(
         &mut self,
         oop: OverflowOp,
@@ -1327,17 +1367,17 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
     pub fn vector_reduce_fmul(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
         unsafe { llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src) }
     }
-    pub fn vector_reduce_fadd_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
+    pub fn vector_reduce_fadd_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src);
-            llvm::LLVMRustSetFastMath(instr);
+            llvm::LLVMRustSetAlgebraicMath(instr);
             instr
         }
     }
-    pub fn vector_reduce_fmul_fast(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
+    pub fn vector_reduce_fmul_algebraic(&mut self, acc: &'ll Value, src: &'ll Value) -> &'ll Value {
         unsafe {
             let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src);
-            llvm::LLVMRustSetFastMath(instr);
+            llvm::LLVMRustSetAlgebraicMath(instr);
             instr
         }
     }
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index 4415c51acf6..3b091fca28b 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1880,14 +1880,14 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
     arith_red!(simd_reduce_mul_ordered: vector_reduce_mul, vector_reduce_fmul, true, mul, 1.0);
     arith_red!(
         simd_reduce_add_unordered: vector_reduce_add,
-        vector_reduce_fadd_fast,
+        vector_reduce_fadd_algebraic,
         false,
         add,
         0.0
     );
     arith_red!(
         simd_reduce_mul_unordered: vector_reduce_mul,
-        vector_reduce_fmul_fast,
+        vector_reduce_fmul_algebraic,
         false,
         mul,
         1.0
diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
index d0044086c61..f9eb1da5dc7 100644
--- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs
@@ -1618,6 +1618,7 @@ extern "C" {
     ) -> &'a Value;
 
     pub fn LLVMRustSetFastMath(Instr: &Value);
+    pub fn LLVMRustSetAlgebraicMath(Instr: &Value);
 
     // Miscellaneous instructions
     pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index e144b1dc1bd..781c54bdef8 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -48,7 +48,7 @@ libc = "0.2.50"
 [dependencies.object]
 version = "0.32.1"
 default-features = false
-features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write"]
+features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"]
 
 [target.'cfg(windows)'.dependencies.windows]
 version = "0.52.0"
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 435b517e602..1ad0dec0640 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};
+use rustc_errors::{DiagCtxt, 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;
@@ -487,7 +487,9 @@ fn collate_raw_dylibs<'a, 'b>(
             }
         }
     }
-    sess.compile_status()?;
+    if let Some(guar) = sess.dcx().has_errors() {
+        return Err(guar);
+    }
     Ok(dylib_table
         .into_iter()
         .map(|(name, imports)| {
@@ -720,10 +722,7 @@ fn link_dwarf_object<'a>(
         Ok(())
     }) {
         Ok(()) => {}
-        Err(e) => {
-            sess.dcx().emit_err(errors::ThorinErrorWrapper(e));
-            sess.dcx().abort_if_errors();
-        }
+        Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)),
     }
 }
 
@@ -999,7 +998,7 @@ fn link_natively<'a>(
                 sess.dcx().emit_note(errors::CheckInstalledVisualStudio);
                 sess.dcx().emit_note(errors::InsufficientVSCodeProduct);
             }
-            sess.dcx().abort_if_errors();
+            FatalError.raise();
         }
     }
 
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 9f06f398288..1f3383815e2 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -626,7 +626,7 @@ impl<'a> Linker for GccLinker<'a> {
                 // it does support --strip-all as a compatibility alias for -s.
                 // The --strip-debug case is handled by running an external
                 // `strip` utility as a separate step after linking.
-                if self.sess.target.os != "illumos" {
+                if !self.sess.target.is_like_solaris {
                     self.linker_arg("--strip-debug");
                 }
             }
diff --git a/compiler/rustc_codegen_ssa/src/back/metadata.rs b/compiler/rustc_codegen_ssa/src/back/metadata.rs
index b683e1b45a8..8e76e47cfef 100644
--- a/compiler/rustc_codegen_ssa/src/back/metadata.rs
+++ b/compiler/rustc_codegen_ssa/src/back/metadata.rs
@@ -15,6 +15,7 @@ use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
 use rustc_metadata::creader::MetadataLoader;
 use rustc_metadata::fs::METADATA_FILENAME;
 use rustc_metadata::EncodedMetadata;
+use rustc_serialize::leb128;
 use rustc_session::Session;
 use rustc_span::sym;
 use rustc_target::abi::Endian;
@@ -420,10 +421,9 @@ pub enum MetadataPosition {
 ///   it's not in an allowlist of otherwise well known dwarf section names to
 ///   go into the final artifact.
 ///
-/// * WebAssembly - we actually don't have any container format for this
-///   target. WebAssembly doesn't support the `dylib` crate type anyway so
-///   there's no need for us to support this at this time. Consequently the
-///   metadata bytes are simply stored as-is into an rlib.
+/// * WebAssembly - this uses wasm files themselves as the object file format
+///   so an empty file with no linking metadata but a single custom section is
+///   created holding our metadata.
 ///
 /// * COFF - Windows-like targets create an object with a section that has
 ///   the `IMAGE_SCN_LNK_REMOVE` flag set which ensures that if the linker
@@ -438,22 +438,13 @@ pub fn create_wrapper_file(
     data: &[u8],
 ) -> (Vec<u8>, MetadataPosition) {
     let Some(mut file) = create_object_file(sess) else {
-        // This is used to handle all "other" targets. This includes targets
-        // in two categories:
-        //
-        // * Some targets don't have support in the `object` crate just yet
-        //   to write an object file. These targets are likely to get filled
-        //   out over time.
-        //
-        // * Targets like WebAssembly don't support dylibs, so the purpose
-        //   of putting metadata in object files, to support linking rlibs
-        //   into dylibs, is moot.
-        //
-        // In both of these cases it means that linking into dylibs will
-        // not be supported by rustc. This doesn't matter for targets like
-        // WebAssembly and for targets not supported by the `object` crate
-        // yet it means that work will need to be done in the `object` crate
-        // to add a case above.
+        if sess.target.is_like_wasm {
+            return (create_metadata_file_for_wasm(data, &section_name), MetadataPosition::First);
+        }
+
+        // Targets using this branch don't have support implemented here yet or
+        // they're not yet implemented in the `object` crate and will likely
+        // fill out this module over time.
         return (data.to_vec(), MetadataPosition::Last);
     };
     let section = if file.format() == BinaryFormat::Xcoff {
@@ -532,6 +523,9 @@ pub fn create_compressed_metadata_file(
     packed_metadata.extend(metadata.raw_data());
 
     let Some(mut file) = create_object_file(sess) else {
+        if sess.target.is_like_wasm {
+            return create_metadata_file_for_wasm(&packed_metadata, b".rustc");
+        }
         return packed_metadata.to_vec();
     };
     if file.format() == BinaryFormat::Xcoff {
@@ -624,3 +618,57 @@ pub fn create_compressed_metadata_file_for_xcoff(
     file.append_section_data(section, data, 1);
     file.write().unwrap()
 }
+
+/// Creates a simple WebAssembly object file, which is itself a wasm module,
+/// that contains a custom section of the name `section_name` with contents
+/// `data`.
+///
+/// NB: the `object` crate does not yet have support for writing the the wasm
+/// object file format. The format is simple enough that for now an extra crate
+/// from crates.io (such as `wasm-encoder`). The file format is:
+///
+/// * 4-byte header "\0asm"
+/// * 4-byte version number - 1u32 in little-endian format
+/// * concatenated sections, which for this object is always "custom sections"
+///
+/// Custom sections are then defined by:
+/// * 1-byte section identifier - 0 for a custom section
+/// * leb-encoded section length (size of the contents beneath this bullet)
+///   * leb-encoded custom section name length
+///   * custom section name
+///   * section contents
+///
+/// One custom section, `linking`, is added here in accordance with
+/// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md>
+/// which is required to inform LLD that this is an object file but it should
+/// otherwise basically ignore it if it otherwise looks at it. The linking
+/// section currently is defined by a single version byte (2) and then further
+/// sections, but we have no more sections, so it's just the byte "2".
+///
+/// The next custom section is the one we're interested in.
+pub fn create_metadata_file_for_wasm(data: &[u8], section_name: &[u8]) -> Vec<u8> {
+    let mut bytes = b"\0asm\x01\0\0\0".to_vec();
+
+    let mut append_custom_section = |section_name: &[u8], data: &[u8]| {
+        let mut section_name_len = [0; leb128::max_leb128_len::<usize>()];
+        let off = leb128::write_usize_leb128(&mut section_name_len, section_name.len());
+        let section_name_len = &section_name_len[..off];
+
+        let mut section_len = [0; leb128::max_leb128_len::<usize>()];
+        let off = leb128::write_usize_leb128(
+            &mut section_len,
+            data.len() + section_name_len.len() + section_name.len(),
+        );
+        let section_len = &section_len[..off];
+
+        bytes.push(0u8);
+        bytes.extend_from_slice(section_len);
+        bytes.extend_from_slice(section_name_len);
+        bytes.extend_from_slice(section_name);
+        bytes.extend_from_slice(data);
+    };
+
+    append_custom_section(b"linking", &[2]);
+    append_custom_section(section_name, data);
+    bytes
+}
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 24fdd013509..7a981217b52 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1856,9 +1856,7 @@ impl SharedEmitterMain {
                 Ok(SharedEmitterMessage::Diagnostic(diag)) => {
                     let dcx = sess.dcx();
                     let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs);
-                    if let Some(code) = diag.code {
-                        d.code(code);
-                    }
+                    d.code = diag.code; // may be `None`, that's ok
                     d.replace_args(diag.args);
                     dcx.emit_diagnostic(d);
                 }
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index 760b3f30ee5..f7afd22a48c 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -449,10 +449,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         let Some(llfn) = cx.declare_c_main(llfty) else {
             // FIXME: We should be smart and show a better diagnostic here.
             let span = cx.tcx().def_span(rust_main_def_id);
-            let dcx = cx.tcx().dcx();
-            dcx.emit_err(errors::MultipleMainFunctions { span });
-            dcx.abort_if_errors();
-            bug!();
+            cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span });
         };
 
         // `main` should respect same config for frame pointer elimination as rest of code
diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
index e4633acd817..82488829b6e 100644
--- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs
@@ -250,6 +250,38 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
                     }
                 }
             }
+            sym::fadd_algebraic
+            | sym::fsub_algebraic
+            | sym::fmul_algebraic
+            | sym::fdiv_algebraic
+            | sym::frem_algebraic => match float_type_width(arg_tys[0]) {
+                Some(_width) => match name {
+                    sym::fadd_algebraic => {
+                        bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
+                    }
+                    sym::fsub_algebraic => {
+                        bx.fsub_algebraic(args[0].immediate(), args[1].immediate())
+                    }
+                    sym::fmul_algebraic => {
+                        bx.fmul_algebraic(args[0].immediate(), args[1].immediate())
+                    }
+                    sym::fdiv_algebraic => {
+                        bx.fdiv_algebraic(args[0].immediate(), args[1].immediate())
+                    }
+                    sym::frem_algebraic => {
+                        bx.frem_algebraic(args[0].immediate(), args[1].immediate())
+                    }
+                    _ => bug!(),
+                },
+                None => {
+                    bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
+                        span,
+                        name,
+                        ty: arg_tys[0],
+                    });
+                    return Ok(());
+                }
+            },
 
             sym::float_to_int_unchecked => {
                 if float_type_width(arg_tys[0]).is_none() {
diff --git a/compiler/rustc_codegen_ssa/src/traits/builder.rs b/compiler/rustc_codegen_ssa/src/traits/builder.rs
index 1c5c78e6ca2..86d3d1260c3 100644
--- a/compiler/rustc_codegen_ssa/src/traits/builder.rs
+++ b/compiler/rustc_codegen_ssa/src/traits/builder.rs
@@ -86,22 +86,27 @@ pub trait BuilderMethods<'a, 'tcx>:
     fn add(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fadd(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fadd_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
+    fn fadd_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn sub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fsub(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fsub_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
+    fn fsub_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn mul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fmul(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fmul_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
+    fn fmul_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn udiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn exactudiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn sdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn exactsdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fdiv(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn fdiv_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
+    fn fdiv_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn urem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn srem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn frem(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn frem_fast(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
+    fn frem_algebraic(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn shl(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn lshr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
     fn ashr(&mut self, lhs: Self::Value, rhs: Self::Value) -> Self::Value;
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 7099cdd5a75..9e4e7911c3a 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -3,10 +3,11 @@ use either::{Left, Right};
 use rustc_hir::def::DefKind;
 use rustc_middle::mir::interpret::{AllocId, ErrorHandled, InterpErrorInfo};
 use rustc_middle::mir::{self, ConstAlloc, ConstValue};
+use rustc_middle::query::TyCtxtAt;
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty::print::with_no_trimmed_paths;
-use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 use rustc_target::abi::{self, Abi};
@@ -87,13 +88,16 @@ fn eval_body_using_ecx<'mir, 'tcx>(
 }
 
 /// The `InterpCx` is only meant to be used to do field and index projections into constants for
-/// `simd_shuffle` and const patterns in match arms. It never performs alignment checks.
+/// `simd_shuffle` and const patterns in match arms.
+///
+/// This should *not* be used to do any actual interpretation. In particular, alignment checks are
+/// turned off!
 ///
 /// The function containing the `match` that is currently being analyzed may have generic bounds
 /// that inform us about the generic bounds of the constant. E.g., using an associated constant
 /// of a function's generic parameter will require knowledge about the bounds on the generic
 /// parameter. These bounds are passed to `mk_eval_cx` via the `ParamEnv` argument.
-pub(crate) fn mk_eval_cx<'mir, 'tcx>(
+pub(crate) fn mk_eval_cx_to_read_const_val<'mir, 'tcx>(
     tcx: TyCtxt<'tcx>,
     root_span: Span,
     param_env: ty::ParamEnv<'tcx>,
@@ -108,6 +112,19 @@ pub(crate) fn mk_eval_cx<'mir, 'tcx>(
     )
 }
 
+/// Create an interpreter context to inspect the given `ConstValue`.
+/// Returns both the context and an `OpTy` that represents the constant.
+pub fn mk_eval_cx_for_const_val<'mir, 'tcx>(
+    tcx: TyCtxtAt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    val: mir::ConstValue<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<(CompileTimeEvalContext<'mir, 'tcx>, OpTy<'tcx>)> {
+    let ecx = mk_eval_cx_to_read_const_val(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
+    let op = ecx.const_val_to_op(val, ty, None).ok()?;
+    Some((ecx, op))
+}
+
 /// This function converts an interpreter value into a MIR constant.
 ///
 /// The `for_diagnostics` flag turns the usual rules for returning `ConstValue::Scalar` into a
@@ -203,7 +220,7 @@ pub(crate) fn turn_into_const_value<'tcx>(
     let def_id = cid.instance.def.def_id();
     let is_static = tcx.is_static(def_id);
     // This is just accessing an already computed constant, so no need to check alignment here.
-    let ecx = mk_eval_cx(
+    let ecx = mk_eval_cx_to_read_const_val(
         tcx,
         tcx.def_span(key.value.instance.def_id()),
         key.param_env,
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index 2c60ede7975..946ffc05cc1 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -393,11 +393,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
                 if ecx.tcx.is_ctfe_mir_available(def) {
                     Ok(ecx.tcx.mir_for_ctfe(def))
                 } else if ecx.tcx.def_kind(def) == DefKind::AssocConst {
-                    let guar = ecx
-                        .tcx
-                        .dcx()
-                        .delayed_bug("This is likely a const item that is missing from its impl");
-                    throw_inval!(AlreadyReported(guar.into()));
+                    ecx.tcx.dcx().bug("This is likely a const item that is missing from its impl");
                 } else {
                     // `find_mir_or_eval_fn` checks that this is a const fn before even calling us,
                     // so this should be unreachable.
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index cd50701040e..289dcb7d01d 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -47,8 +47,7 @@ pub(crate) fn try_destructure_mir_constant_for_user_output<'tcx>(
     ty: Ty<'tcx>,
 ) -> Option<mir::DestructuredConstant<'tcx>> {
     let param_env = ty::ParamEnv::reveal_all();
-    let ecx = mk_eval_cx(tcx.tcx, tcx.span, param_env, CanAccessMutGlobal::No);
-    let op = ecx.const_val_to_op(val, ty, None).ok()?;
+    let (ecx, op) = mk_eval_cx_for_const_val(tcx, param_env, val, ty)?;
 
     // We go to `usize` as we cannot allocate anything bigger anyway.
     let (field_count, variant, down) = match ty.kind() {
diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
index 514a6a7df76..d3428d27d52 100644
--- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs
+++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
 use rustc_span::DUMMY_SP;
 use rustc_target::abi::{Abi, VariantIdx};
 
-use super::eval_queries::{mk_eval_cx, op_to_const};
+use super::eval_queries::{mk_eval_cx_to_read_const_val, op_to_const};
 use super::machine::CompileTimeEvalContext;
 use super::{ValTreeCreationError, ValTreeCreationResult, VALTREE_MAX_NODES};
 use crate::const_eval::CanAccessMutGlobal;
@@ -223,7 +223,7 @@ pub(crate) fn eval_to_valtree<'tcx>(
     let const_alloc = tcx.eval_to_allocation_raw(param_env.and(cid))?;
 
     // FIXME Need to provide a span to `eval_to_valtree`
-    let ecx = mk_eval_cx(
+    let ecx = mk_eval_cx_to_read_const_val(
         tcx,
         DUMMY_SP,
         param_env,
@@ -287,7 +287,8 @@ pub fn valtree_to_const_value<'tcx>(
             }
         }
         ty::Ref(_, inner_ty, _) => {
-            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
+            let mut ecx =
+                mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
             let imm = valtree_to_ref(&mut ecx, valtree, *inner_ty);
             let imm = ImmTy::from_immediate(imm, tcx.layout_of(param_env_ty).unwrap());
             op_to_const(&ecx, &imm.into(), /* for diagnostics */ false)
@@ -314,7 +315,8 @@ pub fn valtree_to_const_value<'tcx>(
                 bug!("could not find non-ZST field during in {layout:#?}");
             }
 
-            let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
+            let mut ecx =
+                mk_eval_cx_to_read_const_val(tcx, DUMMY_SP, param_env, CanAccessMutGlobal::No);
 
             // Need to create a place for this valtree.
             let place = create_valtree_place(&mut ecx, layout, valtree);
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index a3c4734f0a3..effaedd0820 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -249,7 +249,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
         let secondary_errors = mem::take(&mut self.secondary_errors);
         if self.error_emitted.is_none() {
             for error in secondary_errors {
-                error.emit();
+                self.error_emitted = Some(error.emit());
             }
         } else {
             assert!(self.tcx.dcx().has_errors().is_some());
@@ -329,9 +329,7 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> {
 
     fn check_static(&mut self, def_id: DefId, span: Span) {
         if self.tcx.is_thread_local_static(def_id) {
-            self.tcx
-                .dcx()
-                .span_delayed_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
+            self.tcx.dcx().span_bug(span, "tls access is checked in `Rvalue::ThreadLocalRef`");
         }
         self.check_op_spanned(ops::StaticAccess, span)
     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index 25ddd5e85f9..5b4bbf8510b 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -93,6 +93,9 @@ pub struct FnCallNonConst<'tcx> {
 }
 
 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
+    // FIXME: make this translatable
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> {
         let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self;
         let ConstCx { tcx, param_env, .. } = *ccx;
@@ -321,6 +324,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
             .dcx()
             .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
 
+        // FIXME: make this translatable
+        #[allow(rustc::untranslatable_diagnostic)]
         if ccx.is_const_stable_const_fn() {
             err.help("const-stable functions can only call other const-stable functions");
         } else if ccx.tcx.sess.is_nightly_build() {
@@ -591,6 +596,8 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
             span,
             format!("referencing statics in {}s is unstable", ccx.const_kind(),),
         );
+        // FIXME: make this translatable
+        #[allow(rustc::untranslatable_diagnostic)]
         err
             .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.")
             .help("to fix this, the value can be extracted to a `const` and then used.");
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 1ff72516324..cc49e8ea247 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -517,7 +517,7 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> {
 
     fn visit_source_scope(&mut self, scope: SourceScope) {
         if self.body.source_scopes.get(scope).is_none() {
-            self.tcx.dcx().span_delayed_bug(
+            self.tcx.dcx().span_bug(
                 self.body.span,
                 format!(
                     "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
diff --git a/compiler/rustc_const_eval/src/util/caller_location.rs b/compiler/rustc_const_eval/src/util/caller_location.rs
index d1c2d22b5a9..b8e15c485f5 100644
--- a/compiler/rustc_const_eval/src/util/caller_location.rs
+++ b/compiler/rustc_const_eval/src/util/caller_location.rs
@@ -6,7 +6,7 @@ use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::symbol::Symbol;
 use rustc_type_ir::Mutability;
 
-use crate::const_eval::{mk_eval_cx, CanAccessMutGlobal, CompileTimeEvalContext};
+use crate::const_eval::{mk_eval_cx_to_read_const_val, CanAccessMutGlobal, CompileTimeEvalContext};
 use crate::interpret::*;
 
 /// Allocate a `const core::panic::Location` with the provided filename and line/column numbers.
@@ -57,7 +57,12 @@ pub(crate) fn const_caller_location_provider(
     col: u32,
 ) -> mir::ConstValue<'_> {
     trace!("const_caller_location: {}:{}:{}", file, line, col);
-    let mut ecx = mk_eval_cx(tcx.tcx, tcx.span, ty::ParamEnv::reveal_all(), CanAccessMutGlobal::No);
+    let mut ecx = mk_eval_cx_to_read_const_val(
+        tcx.tcx,
+        tcx.span,
+        ty::ParamEnv::reveal_all(),
+        CanAccessMutGlobal::No,
+    );
 
     let loc_place = alloc_caller_location(&mut ecx, file, line, col);
     if intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &loc_place).is_err() {
diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs
index 60f11b1bdd4..692c059beb0 100644
--- a/compiler/rustc_driver_impl/src/lib.rs
+++ b/compiler/rustc_driver_impl/src/lib.rs
@@ -144,16 +144,6 @@ pub const EXIT_FAILURE: i32 = 1;
 pub const DEFAULT_BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
     ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
 
-pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
-    match result {
-        Err(..) => {
-            sess.dcx().abort_if_errors();
-            panic!("error reported but abort_if_errors didn't abort???");
-        }
-        Ok(x) => x,
-    }
-}
-
 pub trait Callbacks {
     /// Called before creating the compiler instance
     fn config(&mut self, _config: &mut interface::Config) {}
@@ -349,27 +339,33 @@ fn run_compiler(
         },
     };
 
-    callbacks.config(&mut config);
-
-    default_early_dcx.abort_if_errors();
     drop(default_early_dcx);
 
+    callbacks.config(&mut config);
+
     interface::run_compiler(config, |compiler| {
         let sess = &compiler.sess;
         let codegen_backend = &*compiler.codegen_backend;
 
+        // This is used for early exits unrelated to errors. E.g. when just
+        // printing some information without compiling, or exiting immediately
+        // after parsing, etc.
+        let early_exit = || {
+            if let Some(guar) = sess.dcx().has_errors() { Err(guar) } else { Ok(()) }
+        };
+
         // This implements `-Whelp`. It should be handled very early, like
         // `--help`/`-Zhelp`/`-Chelp`. This is the earliest it can run, because
         // it must happen after lints are registered, during session creation.
         if sess.opts.describe_lints {
             describe_lints(sess);
-            return sess.compile_status();
+            return early_exit();
         }
 
         let early_dcx = EarlyDiagCtxt::new(sess.opts.error_format);
 
         if print_crate_info(&early_dcx, codegen_backend, sess, has_input) == Compilation::Stop {
-            return sess.compile_status();
+            return early_exit();
         }
 
         if !has_input {
@@ -378,16 +374,16 @@ fn run_compiler(
 
         if !sess.opts.unstable_opts.ls.is_empty() {
             list_metadata(&early_dcx, sess, &*codegen_backend.metadata_loader());
-            return sess.compile_status();
+            return early_exit();
         }
 
         if sess.opts.unstable_opts.link_only {
             process_rlink(sess, compiler);
-            return sess.compile_status();
+            return early_exit();
         }
 
         let linker = compiler.enter(|queries| {
-            let early_exit = || sess.compile_status().map(|_| None);
+            let early_exit = || early_exit().map(|_| None);
             queries.parse()?;
 
             if let Some(ppm) = &sess.opts.pretty {
@@ -659,10 +655,11 @@ fn process_rlink(sess: &Session, compiler: &interface::Compiler) {
                 };
             }
         };
-        let result = compiler.codegen_backend.link(sess, codegen_results, &outputs);
-        abort_on_err(result, sess);
+        if compiler.codegen_backend.link(sess, codegen_results, &outputs).is_err() {
+            FatalError.raise();
+        }
     } else {
-        dcx.emit_fatal(RlinkNotAFile {})
+        dcx.emit_fatal(RlinkNotAFile {});
     }
 }
 
diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs
index e5a7d550115..ff5ffd2454a 100644
--- a/compiler/rustc_driver_impl/src/pretty.rs
+++ b/compiler/rustc_driver_impl/src/pretty.rs
@@ -2,6 +2,7 @@
 
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust as pprust_ast;
+use rustc_errors::FatalError;
 use rustc_hir as hir;
 use rustc_hir_pretty as pprust_hir;
 use rustc_middle::bug;
@@ -18,7 +19,6 @@ use std::fmt::Write;
 
 pub use self::PpMode::*;
 pub use self::PpSourceMode::*;
-use crate::abort_on_err;
 
 struct AstNoAnn;
 
@@ -243,7 +243,9 @@ impl<'tcx> PrintExtra<'tcx> {
 
 pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
     if ppm.needs_analysis() {
-        abort_on_err(ex.tcx().analysis(()), sess);
+        if ex.tcx().analysis(()).is_err() {
+            FatalError.raise();
+        }
     }
 
     let (src, src_name) = get_source(sess);
@@ -334,7 +336,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         ThirTree => {
             let tcx = ex.tcx();
             let mut out = String::new();
-            abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
+            if rustc_hir_analysis::check_crate(tcx).is_err() {
+                FatalError.raise();
+            }
             debug!("pretty printing THIR tree");
             for did in tcx.hir().body_owners() {
                 let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_tree(did));
@@ -344,7 +348,9 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) {
         ThirFlat => {
             let tcx = ex.tcx();
             let mut out = String::new();
-            abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
+            if rustc_hir_analysis::check_crate(tcx).is_err() {
+                FatalError.raise();
+            }
             debug!("pretty printing THIR flat");
             for did in tcx.hir().body_owners() {
                 let _ = writeln!(out, "{:?}:\n{}\n", did, tcx.thir_flat(did));
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 034636bea48..f096f015910 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -1,18 +1,22 @@
 use crate::snippet::Style;
 use crate::{
-    CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level,
-    MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
+    CodeSuggestion, DiagCtxt, DiagnosticMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level,
+    MultiSpan, StashKey, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
 };
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
 use rustc_error_messages::FluentValue;
 use rustc_lint_defs::{Applicability, LintExpectationId};
+use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Symbol;
 use rustc_span::{Span, DUMMY_SP};
 use std::borrow::Cow;
 use std::fmt::{self, Debug};
 use std::hash::{Hash, Hasher};
-use std::panic::Location;
+use std::marker::PhantomData;
+use std::ops::{Deref, DerefMut};
+use std::panic;
+use std::thread::panicking;
 
 /// Error type for `Diagnostic`'s `suggestions` field, indicating that
 /// `.disable_suggestions()` was called on the `Diagnostic`.
@@ -39,6 +43,86 @@ pub enum DiagnosticArgValue {
     StrListSepByAnd(Vec<Cow<'static, str>>),
 }
 
+/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
+/// (or "proof") token that the emission happened.
+pub trait EmissionGuarantee: Sized {
+    /// This exists so that bugs and fatal errors can both result in `!` (an
+    /// abort) when emitted, but have different aborting behaviour.
+    type EmitResult = Self;
+
+    /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each
+    /// `impl` of `EmissionGuarantee`, to make it impossible to create a value
+    /// of `Self::EmitResult` without actually performing the emission.
+    #[track_caller]
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
+}
+
+impl EmissionGuarantee for ErrorGuaranteed {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_error_guaranteed()
+    }
+}
+
+impl EmissionGuarantee for () {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
+    }
+}
+
+/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for
+/// bug diagnostics.
+#[derive(Copy, Clone)]
+pub struct BugAbort;
+
+impl EmissionGuarantee for BugAbort {
+    type EmitResult = !;
+
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
+        panic::panic_any(ExplicitBug);
+    }
+}
+
+/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for
+/// fatal diagnostics.
+#[derive(Copy, Clone)]
+pub struct FatalAbort;
+
+impl EmissionGuarantee for FatalAbort {
+    type EmitResult = !;
+
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
+        crate::FatalError.raise()
+    }
+}
+
+impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
+    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
+        db.emit_producing_nothing();
+        rustc_span::fatal_error::FatalError
+    }
+}
+
+/// Trait implemented by error types. This is rarely implemented manually. Instead, use
+/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
+#[rustc_diagnostic_item = "IntoDiagnostic"]
+pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
+    /// Write out as a diagnostic out of `DiagCtxt`.
+    #[must_use]
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>;
+}
+
+impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
+where
+    T: IntoDiagnostic<'a, G>,
+    G: EmissionGuarantee,
+{
+    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
+        self.node.into_diagnostic(dcx, level).with_span(self.span)
+    }
+}
+
 /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic`
 /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type
 /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*`
@@ -71,17 +155,21 @@ where
     Self: Sized,
 {
     /// Add a subdiagnostic to an existing diagnostic.
-    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+    fn add_to_diagnostic<G: EmissionGuarantee>(self, diag: &mut DiagnosticBuilder<'_, G>) {
         self.add_to_diagnostic_with(diag, |_, m| m);
     }
 
     /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
     /// (to optionally perform eager translation).
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F);
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    );
 }
 
-pub trait SubdiagnosticMessageOp =
-    Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
+pub trait SubdiagnosticMessageOp<G> =
+    Fn(&mut DiagnosticBuilder<'_, G>, SubdiagnosticMessage) -> SubdiagnosticMessage;
 
 /// Trait implemented by lint types. This should not be implemented manually. Instead, use
 /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
@@ -93,32 +181,6 @@ pub trait DecorateLint<'a, G: EmissionGuarantee> {
     fn msg(&self) -> DiagnosticMessage;
 }
 
-#[must_use]
-#[derive(Clone, Debug, Encodable, Decodable)]
-pub struct Diagnostic {
-    // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
-    // outside of what methods in this crate themselves allow.
-    pub(crate) level: Level,
-
-    pub messages: Vec<(DiagnosticMessage, Style)>,
-    pub code: Option<ErrCode>,
-    pub span: MultiSpan,
-    pub children: Vec<SubDiagnostic>,
-    pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
-    args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>,
-
-    /// This is not used for highlighting or rendering any error message. Rather, it can be used
-    /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
-    /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
-    pub sort_span: Span,
-
-    pub is_lint: Option<IsLint>,
-
-    /// With `-Ztrack_diagnostics` enabled,
-    /// we print where in rustc this error was emitted.
-    pub(crate) emitted_at: DiagnosticLocation,
-}
-
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub struct DiagnosticLocation {
     file: Cow<'static, str>,
@@ -129,7 +191,7 @@ pub struct DiagnosticLocation {
 impl DiagnosticLocation {
     #[track_caller]
     fn caller() -> Self {
-        let loc = Location::caller();
+        let loc = panic::Location::caller();
         DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() }
     }
 }
@@ -148,15 +210,6 @@ pub struct IsLint {
     has_future_breakage: bool,
 }
 
-/// A "sub"-diagnostic attached to a parent diagnostic.
-/// For example, a note attached to an error.
-#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
-pub struct SubDiagnostic {
-    pub level: Level,
-    pub messages: Vec<(DiagnosticMessage, Style)>,
-    pub span: MultiSpan,
-}
-
 #[derive(Debug, PartialEq, Eq)]
 pub struct DiagnosticStyledString(pub Vec<StringPart>);
 
@@ -206,6 +259,36 @@ impl StringPart {
     }
 }
 
+/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps
+/// this type, is used for most operations, and should be used instead whenever
+/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime
+/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`.
+#[must_use]
+#[derive(Clone, Debug, Encodable, Decodable)]
+pub struct Diagnostic {
+    // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
+    // outside of what methods in this crate themselves allow.
+    pub(crate) level: Level,
+
+    pub messages: Vec<(DiagnosticMessage, Style)>,
+    pub code: Option<ErrCode>,
+    pub span: MultiSpan,
+    pub children: Vec<SubDiagnostic>,
+    pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
+    args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>,
+
+    /// This is not used for highlighting or rendering any error message. Rather, it can be used
+    /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
+    /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
+    pub sort_span: Span,
+
+    pub is_lint: Option<IsLint>,
+
+    /// With `-Ztrack_diagnostics` enabled,
+    /// we print where in rustc this error was emitted.
+    pub(crate) emitted_at: DiagnosticLocation,
+}
+
 impl Diagnostic {
     #[track_caller]
     pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
@@ -289,6 +372,216 @@ impl Diagnostic {
         }
     }
 
+    // See comment on `DiagnosticBuilder::subdiagnostic_message_to_diagnostic_message`.
+    pub(crate) fn subdiagnostic_message_to_diagnostic_message(
+        &self,
+        attr: impl Into<SubdiagnosticMessage>,
+    ) -> DiagnosticMessage {
+        let msg =
+            self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
+        msg.with_subdiagnostic_message(attr.into())
+    }
+
+    pub(crate) fn sub(
+        &mut self,
+        level: Level,
+        message: impl Into<SubdiagnosticMessage>,
+        span: MultiSpan,
+    ) {
+        let sub = SubDiagnostic {
+            level,
+            messages: vec![(
+                self.subdiagnostic_message_to_diagnostic_message(message),
+                Style::NoStyle,
+            )],
+            span,
+        };
+        self.children.push(sub);
+    }
+
+    pub(crate) fn arg(&mut self, name: impl Into<DiagnosticArgName>, arg: impl IntoDiagnosticArg) {
+        self.args.insert(name.into(), arg.into_diagnostic_arg());
+    }
+
+    pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
+        self.args.iter()
+    }
+
+    pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
+        self.args = args;
+    }
+
+    /// Fields used for Hash, and PartialEq trait.
+    fn keys(
+        &self,
+    ) -> (
+        &Level,
+        &[(DiagnosticMessage, Style)],
+        &Option<ErrCode>,
+        &MultiSpan,
+        &[SubDiagnostic],
+        &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
+        Vec<(&DiagnosticArgName, &DiagnosticArgValue)>,
+        &Option<IsLint>,
+    ) {
+        (
+            &self.level,
+            &self.messages,
+            &self.code,
+            &self.span,
+            &self.children,
+            &self.suggestions,
+            self.args().collect(),
+            // omit self.sort_span
+            &self.is_lint,
+            // omit self.emitted_at
+        )
+    }
+}
+
+impl Hash for Diagnostic {
+    fn hash<H>(&self, state: &mut H)
+    where
+        H: Hasher,
+    {
+        self.keys().hash(state);
+    }
+}
+
+impl PartialEq for Diagnostic {
+    fn eq(&self, other: &Self) -> bool {
+        self.keys() == other.keys()
+    }
+}
+
+/// A "sub"-diagnostic attached to a parent diagnostic.
+/// For example, a note attached to an error.
+#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
+pub struct SubDiagnostic {
+    pub level: Level,
+    pub messages: Vec<(DiagnosticMessage, Style)>,
+    pub span: MultiSpan,
+}
+
+/// Used for emitting structured error messages and other diagnostic information.
+/// Wraps a `Diagnostic`, adding some useful things.
+/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check
+///   that it has been emitted or cancelled.
+/// - The `EmissionGuarantee`, which determines the type returned from `emit`.
+///
+/// Each constructed `DiagnosticBuilder` must be consumed by a function such as
+/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a
+/// `DiagnosticBuilder` is dropped without being consumed by one of these
+/// functions.
+///
+/// If there is some state in a downstream crate you would like to
+/// access in the methods of `DiagnosticBuilder` here, consider
+/// extending `DiagCtxtFlags`.
+#[must_use]
+pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
+    pub dcx: &'a DiagCtxt,
+
+    /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is
+    /// consumed via `emit`, `cancel`, etc. At that point it is consumed and
+    /// replaced with `None`. Then `drop` checks that it is `None`; if not, it
+    /// panics because a diagnostic was built but not used.
+    ///
+    /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is
+    /// often used as a return value, especially within the frequently-used
+    /// `PResult` type. In theory, return value optimization (RVO) should avoid
+    /// unnecessary copying. In practice, it does not (at the time of writing).
+    diag: Option<Box<Diagnostic>>,
+
+    _marker: PhantomData<G>,
+}
+
+// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted
+// twice, which would be bad.
+impl<G> !Clone for DiagnosticBuilder<'_, G> {}
+
+rustc_data_structures::static_assert_size!(
+    DiagnosticBuilder<'_, ()>,
+    2 * std::mem::size_of::<usize>()
+);
+
+impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
+    type Target = Diagnostic;
+
+    fn deref(&self) -> &Diagnostic {
+        self.diag.as_ref().unwrap()
+    }
+}
+
+impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
+    fn deref_mut(&mut self) -> &mut Diagnostic {
+        self.diag.as_mut().unwrap()
+    }
+}
+
+impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.diag.fmt(f)
+    }
+}
+
+/// `DiagnosticBuilder` impls many `&mut self -> &mut Self` methods. Each one
+/// modifies an existing diagnostic, either in a standalone fashion, e.g.
+/// `err.code(code);`, or in a chained fashion to make multiple modifications,
+/// e.g. `err.code(code).span(span);`.
+///
+/// This macro creates an equivalent `self -> Self` method, with a `with_`
+/// prefix. This can be used in a chained fashion when making a new diagnostic,
+/// e.g. `let err = struct_err(msg).with_code(code);`, or emitting a new
+/// diagnostic, e.g. `struct_err(msg).with_code(code).emit();`.
+///
+/// Although the latter method can be used to modify an existing diagnostic,
+/// e.g. `err = err.with_code(code);`, this should be avoided because the former
+/// method gives shorter code, e.g. `err.code(code);`.
+///
+/// Note: the `with_` methods are added only when needed. If you want to use
+/// one and it's not defined, feel free to add it.
+///
+/// Note: any doc comments must be within the `with_fn!` call.
+macro_rules! with_fn {
+    {
+        $with_f:ident,
+        $(#[$attrs:meta])*
+        pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self {
+            $($body:tt)*
+        }
+    } => {
+        // The original function.
+        $(#[$attrs])*
+        #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")]
+        pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self {
+            $($body)*
+        }
+
+        // The `with_*` variant.
+        $(#[$attrs])*
+        #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")]
+        pub fn $with_f(mut $self, $($name: $ty),*) -> Self {
+            $self.$f($($name),*);
+            $self
+        }
+    };
+}
+
+impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
+    #[rustc_lint_diagnostics]
+    #[track_caller]
+    pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self {
+        Self::new_diagnostic(dcx, Diagnostic::new(level, message))
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    #[track_caller]
+    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
+    }
+
     /// Delay emission of this diagnostic as a bug.
     ///
     /// This can be useful in contexts where an error indicates a bug but
@@ -309,6 +602,7 @@ impl Diagnostic {
         self.level = Level::DelayedBug;
     }
 
+    with_fn! { with_span_label,
     /// Appends a labeled span to the diagnostic.
     ///
     /// Labels are used to convey additional context for the diagnostic's primary span. They will
@@ -323,10 +617,12 @@ impl Diagnostic {
     /// primary.
     #[rustc_lint_diagnostics]
     pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self {
-        self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label));
+        let msg = self.subdiagnostic_message_to_diagnostic_message(label);
+        self.span.push_span_label(span, msg);
         self
-    }
+    } }
 
+    with_fn! { with_span_labels,
     /// Labels all the given spans with the provided label.
     /// See [`Self::span_label()`] for more information.
     pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
@@ -334,7 +630,7 @@ impl Diagnostic {
             self.span_label(span, label.to_string());
         }
         self
-    }
+    } }
 
     pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
         let before = self.span.clone();
@@ -412,39 +708,40 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_note,
     /// Add a note attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Note, msg, MultiSpan::new());
         self
-    }
+    } }
 
     fn highlighted_note(&mut self, msg: Vec<StringPart>) -> &mut Self {
         self.sub_with_highlights(Level::Note, msg, MultiSpan::new());
         self
     }
 
-    /// Prints the span with a note above it.
-    /// This is like [`Diagnostic::note()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::note()`], but it's only printed once.
     pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::OnceNote, msg, MultiSpan::new());
         self
     }
 
+    with_fn! { with_span_note,
     /// Prints the span with a note above it.
-    /// This is like [`Diagnostic::note()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::note()`], but it gets its own span.
     #[rustc_lint_diagnostics]
-    pub fn span_note<S: Into<MultiSpan>>(
+    pub fn span_note(
         &mut self,
-        sp: S,
+        sp: impl Into<MultiSpan>,
         msg: impl Into<SubdiagnosticMessage>,
     ) -> &mut Self {
         self.sub(Level::Note, msg, sp.into());
         self
-    }
+    } }
 
     /// Prints the span with a note above it.
-    /// This is like [`Diagnostic::note()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::note_once()`], but it gets its own span.
     pub fn span_note_once<S: Into<MultiSpan>>(
         &mut self,
         sp: S,
@@ -454,15 +751,16 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_warn,
     /// Add a warning attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Warning, msg, MultiSpan::new());
         self
-    }
+    } }
 
     /// Prints the span with a warning above it.
-    /// This is like [`Diagnostic::warn()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::warn()`], but it gets its own span.
     #[rustc_lint_diagnostics]
     pub fn span_warn<S: Into<MultiSpan>>(
         &mut self,
@@ -473,15 +771,15 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_help,
     /// Add a help message attached to this diagnostic.
     #[rustc_lint_diagnostics]
     pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::Help, msg, MultiSpan::new());
         self
-    }
+    } }
 
-    /// Prints the span with a help above it.
-    /// This is like [`Diagnostic::help()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::help()`], but it's only printed once.
     pub fn help_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
         self.sub(Level::OnceHelp, msg, MultiSpan::new());
         self
@@ -494,7 +792,7 @@ impl Diagnostic {
     }
 
     /// Prints the span with some help above it.
-    /// This is like [`Diagnostic::help()`], but it gets its own span.
+    /// This is like [`DiagnosticBuilder::help()`], but it gets its own span.
     #[rustc_lint_diagnostics]
     pub fn span_help<S: Into<MultiSpan>>(
         &mut self,
@@ -531,6 +829,7 @@ impl Diagnostic {
         }
     }
 
+    with_fn! { with_multipart_suggestion,
     /// Show a suggestion that has multiple parts to it.
     /// In other words, multiple changes need to be applied as part of this suggestion.
     pub fn multipart_suggestion(
@@ -545,7 +844,7 @@ impl Diagnostic {
             applicability,
             SuggestionStyle::ShowCode,
         )
-    }
+    } }
 
     /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
     /// In other words, multiple changes need to be applied as part of this suggestion.
@@ -562,7 +861,8 @@ impl Diagnostic {
             SuggestionStyle::ShowAlways,
         )
     }
-    /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
+
+    /// [`DiagnosticBuilder::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
     pub fn multipart_suggestion_with_style(
         &mut self,
         msg: impl Into<SubdiagnosticMessage>,
@@ -619,6 +919,7 @@ impl Diagnostic {
         )
     }
 
+    with_fn! { with_span_suggestion,
     /// Prints out a message with a suggested edit of the code.
     ///
     /// In case of short messages and a simple suggestion, rustc displays it as a label:
@@ -651,9 +952,9 @@ impl Diagnostic {
             SuggestionStyle::ShowCode,
         );
         self
-    }
+    } }
 
-    /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
+    /// [`DiagnosticBuilder::span_suggestion()`] but you can set the [`SuggestionStyle`].
     pub fn span_suggestion_with_style(
         &mut self,
         sp: Span,
@@ -677,6 +978,7 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_span_suggestion_verbose,
     /// Always show the suggested change.
     pub fn span_suggestion_verbose(
         &mut self,
@@ -693,10 +995,11 @@ impl Diagnostic {
             SuggestionStyle::ShowAlways,
         );
         self
-    }
+    } }
 
+    with_fn! { with_span_suggestions,
     /// Prints out a message with multiple suggested edits of the code.
-    /// See also [`Diagnostic::span_suggestion()`].
+    /// See also [`DiagnosticBuilder::span_suggestion()`].
     pub fn span_suggestions(
         &mut self,
         sp: Span,
@@ -711,9 +1014,8 @@ impl Diagnostic {
             applicability,
             SuggestionStyle::ShowCode,
         )
-    }
+    } }
 
-    /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`].
     pub fn span_suggestions_with_style(
         &mut self,
         sp: Span,
@@ -743,7 +1045,7 @@ impl Diagnostic {
 
     /// Prints out a message with multiple suggested edits of the code, where each edit consists of
     /// multiple parts.
-    /// See also [`Diagnostic::multipart_suggestion()`].
+    /// See also [`DiagnosticBuilder::multipart_suggestion()`].
     pub fn multipart_suggestions(
         &mut self,
         msg: impl Into<SubdiagnosticMessage>,
@@ -785,6 +1087,7 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_span_suggestion_short,
     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
     /// inline, it will only show the message and not the suggestion.
     ///
@@ -804,7 +1107,7 @@ impl Diagnostic {
             SuggestionStyle::HideCodeInline,
         );
         self
-    }
+    } }
 
     /// Prints out a message for a suggestion without showing the suggested code.
     ///
@@ -829,6 +1132,7 @@ impl Diagnostic {
         self
     }
 
+    with_fn! { with_tool_only_span_suggestion,
     /// Adds a suggestion to the JSON output that will not be shown in the CLI.
     ///
     /// This is intended to be used for suggestions that are *very* obvious in what the changes
@@ -849,7 +1153,7 @@ impl Diagnostic {
             SuggestionStyle::CompletelyHidden,
         );
         self
-    }
+    } }
 
     /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
     /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
@@ -868,45 +1172,45 @@ impl Diagnostic {
         self
     }
 
-    pub fn span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
+    with_fn! { with_span,
+    /// Add a span.
+    pub fn span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self {
         self.span = sp.into();
         if let Some(span) = self.span.primary_span() {
             self.sort_span = span;
         }
         self
-    }
+    } }
 
     pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self {
         self.is_lint = Some(IsLint { name, has_future_breakage });
         self
     }
 
+    with_fn! { with_code,
+    /// Add an error code.
     pub fn code(&mut self, code: ErrCode) -> &mut Self {
         self.code = Some(code);
         self
-    }
+    } }
 
+    with_fn! { with_primary_message,
+    /// Add a primary message.
     pub fn primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
         self.messages[0] = (msg.into(), Style::NoStyle);
         self
-    }
-
-    pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_>> {
-        self.args.iter()
-    }
+    } }
 
+    with_fn! { with_arg,
+    /// Add an argument.
     pub fn arg(
         &mut self,
         name: impl Into<DiagnosticArgName>,
         arg: impl IntoDiagnosticArg,
     ) -> &mut Self {
-        self.args.insert(name.into(), arg.into_diagnostic_arg());
+        self.deref_mut().arg(name, arg);
         self
-    }
-
-    pub fn replace_args(&mut self, args: FxIndexMap<DiagnosticArgName, DiagnosticArgValue>) {
-        self.args = args;
-    }
+    } }
 
     /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
     /// combining it with the primary message of the diagnostic (if translatable, otherwise it just
@@ -915,9 +1219,7 @@ impl Diagnostic {
         &self,
         attr: impl Into<SubdiagnosticMessage>,
     ) -> DiagnosticMessage {
-        let msg =
-            self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
-        msg.with_subdiagnostic_message(attr.into())
+        self.deref().subdiagnostic_message_to_diagnostic_message(attr)
     }
 
     /// Convenience function for internal use, clients should use one of the
@@ -925,15 +1227,7 @@ impl Diagnostic {
     ///
     /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
     pub fn sub(&mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan) {
-        let sub = SubDiagnostic {
-            level,
-            messages: vec![(
-                self.subdiagnostic_message_to_diagnostic_message(message),
-                Style::NoStyle,
-            )],
-            span,
-        };
-        self.children.push(sub);
+        self.deref_mut().sub(level, message, span);
     }
 
     /// Convenience function for internal use, clients should use one of the
@@ -947,45 +1241,111 @@ impl Diagnostic {
         self.children.push(sub);
     }
 
-    /// Fields used for Hash, and PartialEq trait
-    fn keys(
-        &self,
-    ) -> (
-        &Level,
-        &[(DiagnosticMessage, Style)],
-        &Option<ErrCode>,
-        &MultiSpan,
-        &[SubDiagnostic],
-        &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
-        Vec<(&DiagnosticArgName, &DiagnosticArgValue)>,
-        &Option<IsLint>,
-    ) {
-        (
-            &self.level,
-            &self.messages,
-            &self.code,
-            &self.span,
-            &self.children,
-            &self.suggestions,
-            self.args().collect(),
-            // omit self.sort_span
-            &self.is_lint,
-            // omit self.emitted_at
-        )
+    /// Takes the diagnostic. For use by methods that consume the
+    /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the
+    /// only code that will be run on `self`.
+    fn take_diag(&mut self) -> Diagnostic {
+        Box::into_inner(self.diag.take().unwrap())
     }
-}
 
-impl Hash for Diagnostic {
-    fn hash<H>(&self, state: &mut H)
-    where
-        H: Hasher,
-    {
-        self.keys().hash(state);
+    /// Most `emit_producing_guarantee` functions use this as a starting point.
+    fn emit_producing_nothing(mut self) {
+        let diag = self.take_diag();
+        self.dcx.emit_diagnostic(diag);
+    }
+
+    /// `ErrorGuaranteed::emit_producing_guarantee` uses this.
+    fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
+        let diag = self.take_diag();
+
+        // The only error levels that produce `ErrorGuaranteed` are
+        // `Error` and `DelayedBug`. But `DelayedBug` should never occur here
+        // because delayed bugs have their level changed to `Bug` when they are
+        // actually printed, so they produce an ICE.
+        //
+        // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could
+        // be overwritten with a new one thanks to `DerefMut`. So this assert
+        // protects against that, too.)
+        assert!(
+            matches!(diag.level, Level::Error | Level::DelayedBug),
+            "invalid diagnostic level ({:?})",
+            diag.level,
+        );
+
+        let guar = self.dcx.emit_diagnostic(diag);
+        guar.unwrap()
+    }
+
+    /// Emit and consume the diagnostic.
+    #[track_caller]
+    pub fn emit(self) -> G::EmitResult {
+        G::emit_producing_guarantee(self)
+    }
+
+    /// Emit the diagnostic unless `delay` is true,
+    /// in which case the emission will be delayed as a bug.
+    ///
+    /// See `emit` and `delay_as_bug` for details.
+    #[track_caller]
+    pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
+        if delay {
+            self.downgrade_to_delayed_bug();
+        }
+        self.emit()
+    }
+
+    /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or
+    /// cancelled or it will panic when dropped).
+    pub fn cancel(mut self) {
+        self.diag = None;
+        drop(self);
+    }
+
+    /// Stashes diagnostic for possible later improvement in a different,
+    /// later stage of the compiler. The diagnostic can be accessed with
+    /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
+    pub fn stash(mut self, span: Span, key: StashKey) {
+        self.dcx.stash_diagnostic(span, key, self.take_diag());
+    }
+
+    /// Delay emission of this diagnostic as a bug.
+    ///
+    /// This can be useful in contexts where an error indicates a bug but
+    /// typically this only happens when other compilation errors have already
+    /// happened. In those cases this can be used to defer emission of this
+    /// diagnostic as a bug in the compiler only if no other errors have been
+    /// emitted.
+    ///
+    /// In the meantime, though, callsites are required to deal with the "bug"
+    /// locally in whichever way makes the most sense.
+    #[track_caller]
+    pub fn delay_as_bug(mut self) -> G::EmitResult {
+        self.downgrade_to_delayed_bug();
+        self.emit()
     }
 }
 
-impl PartialEq for Diagnostic {
-    fn eq(&self, other: &Self) -> bool {
-        self.keys() == other.keys()
+/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted,
+/// cancelled, etc.) or we emit a bug.
+impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
+    fn drop(&mut self) {
+        match self.diag.take() {
+            Some(diag) if !panicking() => {
+                self.dcx.emit_diagnostic(Diagnostic::new(
+                    Level::Bug,
+                    DiagnosticMessage::from("the following error was constructed but not emitted"),
+                ));
+                self.dcx.emit_diagnostic(*diag);
+                panic!("error was constructed but not emitted");
+            }
+            _ => {}
+        }
     }
 }
+
+#[macro_export]
+macro_rules! struct_span_code_err {
+    ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
+        $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
+    })
+}
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
deleted file mode 100644
index 0572df69ca9..00000000000
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ /dev/null
@@ -1,441 +0,0 @@
-use crate::diagnostic::IntoDiagnosticArg;
-use crate::{DiagCtxt, Level, MultiSpan, StashKey};
-use crate::{
-    Diagnostic, DiagnosticMessage, DiagnosticStyledString, ErrCode, ErrorGuaranteed, ExplicitBug,
-    SubdiagnosticMessage,
-};
-use rustc_lint_defs::Applicability;
-use rustc_span::source_map::Spanned;
-
-use rustc_span::Span;
-use std::borrow::Cow;
-use std::fmt::{self, Debug};
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-use std::panic;
-use std::thread::panicking;
-
-/// Trait implemented by error types. This is rarely implemented manually. Instead, use
-/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic].
-#[rustc_diagnostic_item = "IntoDiagnostic"]
-pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> {
-    /// Write out as a diagnostic out of `DiagCtxt`.
-    #[must_use]
-    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>;
-}
-
-impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned<T>
-where
-    T: IntoDiagnostic<'a, G>,
-    G: EmissionGuarantee,
-{
-    fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> {
-        self.node.into_diagnostic(dcx, level).with_span(self.span)
-    }
-}
-
-/// Used for emitting structured error messages and other diagnostic information.
-/// Each constructed `DiagnosticBuilder` must be consumed by a function such as
-/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a
-/// `DiagnosticBuilder` is dropped without being consumed by one of these
-/// functions.
-///
-/// If there is some state in a downstream crate you would like to
-/// access in the methods of `DiagnosticBuilder` here, consider
-/// extending `DiagCtxtFlags`.
-#[must_use]
-pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> {
-    pub dcx: &'a DiagCtxt,
-
-    /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is
-    /// consumed via `emit`, `cancel`, etc. At that point it is consumed and
-    /// replaced with `None`. Then `drop` checks that it is `None`; if not, it
-    /// panics because a diagnostic was built but not used.
-    ///
-    /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is
-    /// often used as a return value, especially within the frequently-used
-    /// `PResult` type. In theory, return value optimization (RVO) should avoid
-    /// unnecessary copying. In practice, it does not (at the time of writing).
-    diag: Option<Box<Diagnostic>>,
-
-    _marker: PhantomData<G>,
-}
-
-// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted
-// twice, which would be bad.
-impl<G> !Clone for DiagnosticBuilder<'_, G> {}
-
-rustc_data_structures::static_assert_size!(
-    DiagnosticBuilder<'_, ()>,
-    2 * std::mem::size_of::<usize>()
-);
-
-/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
-/// (or "proof") token that the emission happened.
-pub trait EmissionGuarantee: Sized {
-    /// This exists so that bugs and fatal errors can both result in `!` (an
-    /// abort) when emitted, but have different aborting behaviour.
-    type EmitResult = Self;
-
-    /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each
-    /// `impl` of `EmissionGuarantee`, to make it impossible to create a value
-    /// of `Self::EmitResult` without actually performing the emission.
-    #[track_caller]
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult;
-}
-
-impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
-    /// Takes the diagnostic. For use by methods that consume the
-    /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the
-    /// only code that will be run on `self`.
-    fn take_diag(&mut self) -> Diagnostic {
-        Box::into_inner(self.diag.take().unwrap())
-    }
-
-    /// Most `emit_producing_guarantee` functions use this as a starting point.
-    fn emit_producing_nothing(mut self) {
-        let diag = self.take_diag();
-        self.dcx.emit_diagnostic(diag);
-    }
-
-    /// `ErrorGuaranteed::emit_producing_guarantee` uses this.
-    fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed {
-        let diag = self.take_diag();
-
-        // The only error levels that produce `ErrorGuaranteed` are
-        // `Error` and `DelayedBug`. But `DelayedBug` should never occur here
-        // because delayed bugs have their level changed to `Bug` when they are
-        // actually printed, so they produce an ICE.
-        //
-        // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could
-        // be overwritten with a new one thanks to `DerefMut`. So this assert
-        // protects against that, too.)
-        assert!(
-            matches!(diag.level, Level::Error | Level::DelayedBug),
-            "invalid diagnostic level ({:?})",
-            diag.level,
-        );
-
-        let guar = self.dcx.emit_diagnostic(diag);
-        guar.unwrap()
-    }
-}
-
-impl EmissionGuarantee for ErrorGuaranteed {
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        db.emit_producing_error_guaranteed()
-    }
-}
-
-impl EmissionGuarantee for () {
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        db.emit_producing_nothing();
-    }
-}
-
-/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for
-/// bug diagnostics.
-#[derive(Copy, Clone)]
-pub struct BugAbort;
-
-impl EmissionGuarantee for BugAbort {
-    type EmitResult = !;
-
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        db.emit_producing_nothing();
-        panic::panic_any(ExplicitBug);
-    }
-}
-
-/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for
-/// fatal diagnostics.
-#[derive(Copy, Clone)]
-pub struct FatalAbort;
-
-impl EmissionGuarantee for FatalAbort {
-    type EmitResult = !;
-
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        db.emit_producing_nothing();
-        crate::FatalError.raise()
-    }
-}
-
-impl EmissionGuarantee for rustc_span::fatal_error::FatalError {
-    fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult {
-        db.emit_producing_nothing();
-        rustc_span::fatal_error::FatalError
-    }
-}
-
-/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and
-/// methods of the embedded `Diagnostic`. However, that doesn't allow method
-/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines
-/// two builder methods at that level, both of which wrap the equivalent method
-/// in `Diagnostic`.
-/// - A `&mut self -> &mut Self` method, with the same name as the underlying
-///   `Diagnostic` method. It is mostly to modify existing diagnostics, either
-///   in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion
-///   to make multiple modifications, e.g. `err.code(code).span(span)`.
-/// - A `self -> Self` method, which has a `with_` prefix added.
-///   It is mostly used in a chained fashion when producing a new diagnostic,
-///   e.g. `let err = struct_err(msg).with_code(code)`, or when emitting a new
-///   diagnostic , e.g. `struct_err(msg).with_code(code).emit()`.
-///
-/// Although the latter method can be used to modify an existing diagnostic,
-/// e.g. `err = err.with_code(code)`, this should be avoided because the former
-/// method gives shorter code, e.g. `err.code(code)`.
-macro_rules! forward {
-    (
-        ($f:ident, $with_f:ident)($($name:ident: $ty:ty),* $(,)?)
-    ) => {
-        #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
-        pub fn $f(&mut self, $($name: $ty),*) -> &mut Self {
-            self.diag.as_mut().unwrap().$f($($name),*);
-            self
-        }
-        #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")]
-        pub fn $with_f(mut self, $($name: $ty),*) -> Self {
-            self.diag.as_mut().unwrap().$f($($name),*);
-            self
-        }
-    };
-}
-
-impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
-    type Target = Diagnostic;
-
-    fn deref(&self) -> &Diagnostic {
-        self.diag.as_ref().unwrap()
-    }
-}
-
-impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
-    fn deref_mut(&mut self) -> &mut Diagnostic {
-        self.diag.as_mut().unwrap()
-    }
-}
-
-impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
-    #[rustc_lint_diagnostics]
-    #[track_caller]
-    pub fn new<M: Into<DiagnosticMessage>>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self {
-        Self::new_diagnostic(dcx, Diagnostic::new(level, message))
-    }
-
-    /// Creates a new `DiagnosticBuilder` with an already constructed
-    /// diagnostic.
-    #[track_caller]
-    pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self {
-        debug!("Created new diagnostic");
-        Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData }
-    }
-
-    /// Emit and consume the diagnostic.
-    #[track_caller]
-    pub fn emit(self) -> G::EmitResult {
-        G::emit_producing_guarantee(self)
-    }
-
-    /// Emit the diagnostic unless `delay` is true,
-    /// in which case the emission will be delayed as a bug.
-    ///
-    /// See `emit` and `delay_as_bug` for details.
-    #[track_caller]
-    pub fn emit_unless(mut self, delay: bool) -> G::EmitResult {
-        if delay {
-            self.downgrade_to_delayed_bug();
-        }
-        self.emit()
-    }
-
-    /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or
-    /// cancelled or it will panic when dropped).
-    pub fn cancel(mut self) {
-        self.diag = None;
-        drop(self);
-    }
-
-    /// Stashes diagnostic for possible later improvement in a different,
-    /// later stage of the compiler. The diagnostic can be accessed with
-    /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`].
-    pub fn stash(mut self, span: Span, key: StashKey) {
-        self.dcx.stash_diagnostic(span, key, self.take_diag());
-    }
-
-    /// Delay emission of this diagnostic as a bug.
-    ///
-    /// This can be useful in contexts where an error indicates a bug but
-    /// typically this only happens when other compilation errors have already
-    /// happened. In those cases this can be used to defer emission of this
-    /// diagnostic as a bug in the compiler only if no other errors have been
-    /// emitted.
-    ///
-    /// In the meantime, though, callsites are required to deal with the "bug"
-    /// locally in whichever way makes the most sense.
-    #[track_caller]
-    pub fn delay_as_bug(mut self) -> G::EmitResult {
-        self.downgrade_to_delayed_bug();
-        self.emit()
-    }
-
-    forward!((span_label, with_span_label)(
-        span: Span,
-        label: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((span_labels, with_span_labels)(
-        spans: impl IntoIterator<Item = Span>,
-        label: &str,
-    ));
-    forward!((note_expected_found, with_note_expected_found)(
-        expected_label: &dyn fmt::Display,
-        expected: DiagnosticStyledString,
-        found_label: &dyn fmt::Display,
-        found: DiagnosticStyledString,
-    ));
-    forward!((note_expected_found_extra, with_note_expected_found_extra)(
-        expected_label: &dyn fmt::Display,
-        expected: DiagnosticStyledString,
-        found_label: &dyn fmt::Display,
-        found: DiagnosticStyledString,
-        expected_extra: &dyn fmt::Display,
-        found_extra: &dyn fmt::Display,
-    ));
-    forward!((note, with_note)(
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((note_once, with_note_once)(
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((span_note, with_span_note)(
-        sp: impl Into<MultiSpan>,
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((span_note_once, with_span_note_once)(
-        sp: impl Into<MultiSpan>,
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((warn, with_warn)(
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((span_warn, with_span_warn)(
-        sp: impl Into<MultiSpan>,
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((help, with_help)(
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((help_once, with_help_once)(
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((span_help, with_span_help_once)(
-        sp: impl Into<MultiSpan>,
-        msg: impl Into<SubdiagnosticMessage>,
-    ));
-    forward!((multipart_suggestion, with_multipart_suggestion)(
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: Vec<(Span, String)>,
-        applicability: Applicability,
-    ));
-    forward!((multipart_suggestion_verbose, with_multipart_suggestion_verbose)(
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: Vec<(Span, String)>,
-        applicability: Applicability,
-    ));
-    forward!((tool_only_multipart_suggestion, with_tool_only_multipart_suggestion)(
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: Vec<(Span, String)>,
-        applicability: Applicability,
-    ));
-    forward!((span_suggestion, with_span_suggestion)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: impl ToString,
-        applicability: Applicability,
-    ));
-    forward!((span_suggestions, with_span_suggestions)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestions: impl IntoIterator<Item = String>,
-        applicability: Applicability,
-    ));
-    forward!((multipart_suggestions, with_multipart_suggestions)(
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
-        applicability: Applicability,
-    ));
-    forward!((span_suggestion_short, with_span_suggestion_short)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: impl ToString,
-        applicability: Applicability,
-    ));
-    forward!((span_suggestion_verbose, with_span_suggestion_verbose)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: impl ToString,
-        applicability: Applicability,
-    ));
-    forward!((span_suggestion_hidden, with_span_suggestion_hidden)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: impl ToString,
-        applicability: Applicability,
-    ));
-    forward!((tool_only_span_suggestion, with_tool_only_span_suggestion)(
-        sp: Span,
-        msg: impl Into<SubdiagnosticMessage>,
-        suggestion: impl ToString,
-        applicability: Applicability,
-    ));
-    forward!((primary_message, with_primary_message)(
-        msg: impl Into<DiagnosticMessage>,
-    ));
-    forward!((span, with_span)(
-        sp: impl Into<MultiSpan>,
-    ));
-    forward!((is_lint, with_is_lint)(
-        name: String, has_future_breakage: bool,
-    ));
-    forward!((code, with_code)(
-        code: ErrCode,
-    ));
-    forward!((arg, with_arg)(
-        name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
-    ));
-    forward!((subdiagnostic, with_subdiagnostic)(
-        dcx: &DiagCtxt,
-        subdiagnostic: impl crate::AddToDiagnostic,
-    ));
-}
-
-impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.diag.fmt(f)
-    }
-}
-
-/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted,
-/// cancelled, etc.) or we emit a bug.
-impl<G: EmissionGuarantee> Drop for DiagnosticBuilder<'_, G> {
-    fn drop(&mut self) {
-        match self.diag.take() {
-            Some(diag) if !panicking() => {
-                self.dcx.emit_diagnostic(Diagnostic::new(
-                    Level::Bug,
-                    DiagnosticMessage::from("the following error was constructed but not emitted"),
-                ));
-                self.dcx.emit_diagnostic(*diag);
-                panic!("error was constructed but not emitted");
-            }
-            _ => {}
-        }
-    }
-}
-
-#[macro_export]
-macro_rules! struct_span_code_err {
-    ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({
-        $dcx.struct_span_err($span, format!($($message)*)).with_code($code)
-    })
-}
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index eaf75539f59..bc1e81642ff 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -299,7 +299,11 @@ pub struct SingleLabelManySpans {
     pub label: &'static str,
 }
 impl AddToDiagnostic for SingleLabelManySpans {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut crate::Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         diag.span_labels(self.spans, self.label);
     }
 }
@@ -312,23 +316,6 @@ pub struct ExpectedLifetimeParameter {
     pub count: usize,
 }
 
-#[derive(Subdiagnostic)]
-#[note(errors_delayed_at_with_newline)]
-pub struct DelayedAtWithNewline {
-    #[primary_span]
-    pub span: Span,
-    pub emitted_at: DiagnosticLocation,
-    pub note: Backtrace,
-}
-#[derive(Subdiagnostic)]
-#[note(errors_delayed_at_without_newline)]
-pub struct DelayedAtWithoutNewline {
-    #[primary_span]
-    pub span: Span,
-    pub emitted_at: DiagnosticLocation,
-    pub note: Backtrace,
-}
-
 impl IntoDiagnosticArg for DiagnosticLocation {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue {
         DiagnosticArgValue::Str(Cow::from(self.to_string()))
@@ -341,13 +328,6 @@ impl IntoDiagnosticArg for Backtrace {
     }
 }
 
-#[derive(Subdiagnostic)]
-#[note(errors_invalid_flushed_delayed_diagnostic_level)]
-pub struct InvalidFlushedDelayedDiagnosticLevel {
-    #[primary_span]
-    pub span: Span,
-    pub level: Level,
-}
 impl IntoDiagnosticArg for Level {
     fn into_diagnostic_arg(self) -> DiagnosticArgValue {
         DiagnosticArgValue::Str(Cow::from(self.to_string()))
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index e09c041c1d0..df94b69004b 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -599,7 +599,7 @@ impl Emitter for SilentEmitter {
 
     fn emit_diagnostic(&mut self, mut diag: Diagnostic) {
         if diag.level == Level::Fatal {
-            diag.note(self.fatal_note.clone());
+            diag.sub(Level::Note, self.fatal_note.clone(), MultiSpan::new());
             self.fatal_dcx.emit_diagnostic(diag);
         }
     }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 064ea8d7516..7e3d15ffc92 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -37,16 +37,13 @@ extern crate self as rustc_errors;
 
 pub use codes::*;
 pub use diagnostic::{
-    AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName,
-    DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, StringPart, SubDiagnostic,
-    SubdiagnosticMessageOp,
-};
-pub use diagnostic_builder::{
-    BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic,
+    AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName,
+    DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString, EmissionGuarantee, FatalAbort,
+    IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic, SubdiagnosticMessageOp,
 };
 pub use diagnostic_impls::{
     DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter,
-    IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans,
+    IndicateAnonymousLifetime, SingleLabelManySpans,
 };
 pub use emitter::ColorConfig;
 pub use rustc_error_messages::{
@@ -62,7 +59,6 @@ pub use snippet::Style;
 // See https://github.com/rust-lang/rust/pull/115393.
 pub use termcolor::{Color, ColorSpec, WriteColor};
 
-use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline};
 use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter};
 use registry::Registry;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
@@ -88,7 +84,6 @@ use Level::*;
 pub mod annotate_snippet_emitter_writer;
 pub mod codes;
 mod diagnostic;
-mod diagnostic_builder;
 mod diagnostic_impls;
 pub mod emitter;
 pub mod error;
@@ -476,9 +471,10 @@ struct DiagCtxtInner {
     emitted_diagnostics: FxHashSet<Hash128>,
 
     /// Stashed diagnostics emitted in one stage of the compiler that may be
-    /// stolen by other stages (e.g. to improve them and add more information).
-    /// The stashed diagnostics count towards the total error count.
-    /// When `.abort_if_errors()` is called, these are also emitted.
+    /// stolen and emitted/cancelled by other stages (e.g. to improve them and
+    /// add more information). All stashed diagnostics must be emitted with
+    /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped,
+    /// otherwise an assertion failure will occur.
     stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>,
 
     future_breakage_diagnostics: Vec<Diagnostic>,
@@ -563,7 +559,9 @@ pub struct DiagCtxtFlags {
 
 impl Drop for DiagCtxtInner {
     fn drop(&mut self) {
-        self.emit_stashed_diagnostics();
+        // Any stashed diagnostics should have been handled by
+        // `emit_stashed_diagnostics` by now.
+        assert!(self.stashed_diagnostics.is_empty());
 
         if self.err_guars.is_empty() {
             self.flush_delayed()
@@ -755,17 +753,24 @@ impl DiagCtxt {
     }
 
     /// Emit all stashed diagnostics.
-    pub fn emit_stashed_diagnostics(&self) {
+    pub fn emit_stashed_diagnostics(&self) -> Option<ErrorGuaranteed> {
         self.inner.borrow_mut().emit_stashed_diagnostics()
     }
 
-    /// This excludes lint errors, delayed bugs, and stashed errors.
+    /// This excludes lint errors, delayed bugs and stashed errors.
     #[inline]
-    pub fn err_count(&self) -> usize {
+    pub fn err_count_excluding_lint_errs(&self) -> usize {
         self.inner.borrow().err_guars.len()
     }
 
-    /// This excludes normal errors, lint errors and delayed bugs. Unless
+    /// This excludes delayed bugs and stashed errors.
+    #[inline]
+    pub fn err_count(&self) -> usize {
+        let inner = self.inner.borrow();
+        inner.err_guars.len() + inner.lint_err_guars.len()
+    }
+
+    /// This excludes normal errors, lint errors, and delayed bugs. Unless
     /// absolutely necessary, avoid using this. It's dubious because stashed
     /// errors can later be cancelled, so the presence of a stashed error at
     /// some point of time doesn't guarantee anything -- there are no
@@ -774,27 +779,29 @@ impl DiagCtxt {
         self.inner.borrow().stashed_err_count
     }
 
-    /// This excludes lint errors, delayed bugs, and stashed errors.
-    pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
-        self.inner.borrow().has_errors()
+    /// This excludes lint errors, delayed bugs, and stashed errors. Unless
+    /// absolutely necessary, prefer `has_errors` to this method.
+    pub fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
+        self.inner.borrow().has_errors_excluding_lint_errors()
     }
 
-    /// This excludes delayed bugs and stashed errors. Unless absolutely
-    /// necessary, prefer `has_errors` to this method.
-    pub fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
-        self.inner.borrow().has_errors_or_lint_errors()
+    /// This excludes delayed bugs and stashed errors.
+    pub fn has_errors(&self) -> Option<ErrorGuaranteed> {
+        self.inner.borrow().has_errors()
     }
 
     /// This excludes stashed errors. Unless absolutely necessary, prefer
-    /// `has_errors` or `has_errors_or_lint_errors` to this method.
-    pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
-        self.inner.borrow().has_errors_or_lint_errors_or_delayed_bugs()
+    /// `has_errors` to this method.
+    pub fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
+        self.inner.borrow().has_errors_or_delayed_bugs()
     }
 
     pub fn print_error_count(&self, registry: &Registry) {
         let mut inner = self.inner.borrow_mut();
 
-        inner.emit_stashed_diagnostics();
+        // Any stashed diagnostics should have been handled by
+        // `emit_stashed_diagnostics` by now.
+        assert!(inner.stashed_diagnostics.is_empty());
 
         if inner.treat_err_as_bug() {
             return;
@@ -869,10 +876,12 @@ impl DiagCtxt {
         }
     }
 
+    /// This excludes delayed bugs and stashed errors. Used for early aborts
+    /// after errors occurred -- e.g. because continuing in the face of errors is
+    /// likely to lead to bad results, such as spurious/uninteresting
+    /// additional errors -- when returning an error `Result` is difficult.
     pub fn abort_if_errors(&self) {
-        let mut inner = self.inner.borrow_mut();
-        inner.emit_stashed_diagnostics();
-        if !inner.err_guars.is_empty() {
+        if self.has_errors().is_some() {
             FatalError.raise();
         }
     }
@@ -1273,10 +1282,10 @@ impl DiagCtxt {
 // `DiagCtxtInner::foo`.
 impl DiagCtxtInner {
     /// Emit all stashed diagnostics.
-    fn emit_stashed_diagnostics(&mut self) {
+    fn emit_stashed_diagnostics(&mut self) -> Option<ErrorGuaranteed> {
+        let mut guar = None;
         let has_errors = !self.err_guars.is_empty();
         for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() {
-            // Decrement the count tracking the stash; emitting will increment it.
             if diag.is_error() {
                 if diag.is_lint.is_none() {
                     self.stashed_err_count -= 1;
@@ -1289,8 +1298,9 @@ impl DiagCtxtInner {
                     continue;
                 }
             }
-            self.emit_diagnostic(diag);
+            guar = guar.or(self.emit_diagnostic(diag));
         }
+        guar
     }
 
     // Return value is only `Some` if the level is `Error` or `DelayedBug`.
@@ -1334,7 +1344,7 @@ impl DiagCtxtInner {
             DelayedBug => {
                 // If we have already emitted at least one error, we don't need
                 // to record the delayed bug, because it'll never be used.
-                return if let Some(guar) = self.has_errors_or_lint_errors() {
+                return if let Some(guar) = self.has_errors() {
                     Some(guar)
                 } else {
                     let backtrace = std::backtrace::Backtrace::capture();
@@ -1395,9 +1405,8 @@ impl DiagCtxtInner {
                 };
                 diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {});
                 if already_emitted {
-                    diagnostic.note(
-                        "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`",
-                    );
+                    let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`";
+                    diagnostic.sub(Level::Note, msg, MultiSpan::new());
                 }
 
                 if is_error {
@@ -1451,17 +1460,16 @@ impl DiagCtxtInner {
             .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get())
     }
 
-    fn has_errors(&self) -> Option<ErrorGuaranteed> {
+    fn has_errors_excluding_lint_errors(&self) -> Option<ErrorGuaranteed> {
         self.err_guars.get(0).copied()
     }
 
-    fn has_errors_or_lint_errors(&self) -> Option<ErrorGuaranteed> {
-        self.has_errors().or_else(|| self.lint_err_guars.get(0).copied())
+    fn has_errors(&self) -> Option<ErrorGuaranteed> {
+        self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied())
     }
 
-    fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
-        self.has_errors_or_lint_errors()
-            .or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
+    fn has_errors_or_delayed_bugs(&self) -> Option<ErrorGuaranteed> {
+        self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
     }
 
     /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`.
@@ -1483,7 +1491,22 @@ impl DiagCtxtInner {
         self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string()
     }
 
+    fn eagerly_translate_for_subdiag(
+        &self,
+        diag: &Diagnostic,
+        msg: impl Into<SubdiagnosticMessage>,
+    ) -> SubdiagnosticMessage {
+        let args = diag.args();
+        let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
+        self.eagerly_translate(msg, args)
+    }
+
     fn flush_delayed(&mut self) {
+        // Stashed diagnostics must be emitted before delayed bugs are flushed.
+        // Otherwise, we might ICE prematurely when errors would have
+        // eventually happened.
+        assert!(self.stashed_diagnostics.is_empty());
+
         if self.delayed_bugs.is_empty() {
             return;
         }
@@ -1527,17 +1550,14 @@ impl DiagCtxtInner {
             if bug.level != DelayedBug {
                 // NOTE(eddyb) not panicking here because we're already producing
                 // an ICE, and the more information the merrier.
-                let subdiag = InvalidFlushedDelayedDiagnosticLevel {
-                    span: bug.span.primary_span().unwrap(),
-                    level: bug.level,
-                };
-                // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
-                // just uses `DiagCtxtInner` functions.
-                subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| {
-                    let args = diag.args();
-                    let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
-                    self.eagerly_translate(msg, args)
-                });
+                //
+                // We are at the `Diagnostic`/`DiagCtxtInner` level rather than
+                // the usual `DiagnosticBuilder`/`DiagCtxt` level, so we must
+                // augment `bug` in a lower-level fashion.
+                bug.arg("level", bug.level);
+                let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level;
+                let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call
+                bug.sub(Level::Note, msg, bug.span.primary_span().unwrap().into());
             }
             bug.level = Bug;
 
@@ -1571,39 +1591,22 @@ impl DelayedDiagnostic {
         DelayedDiagnostic { inner: diagnostic, note: backtrace }
     }
 
-    fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic {
-        // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
-        // just uses `DiagCtxtInner` functions.
-        let subdiag_with = |diag: &mut Diagnostic, msg| {
-            let args = diag.args();
-            let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
-            dcx.eagerly_translate(msg, args)
-        };
-
-        match self.note.status() {
-            BacktraceStatus::Captured => {
-                let inner = &self.inner;
-                let subdiag = DelayedAtWithNewline {
-                    span: inner.span.primary_span().unwrap_or(DUMMY_SP),
-                    emitted_at: inner.emitted_at.clone(),
-                    note: self.note,
-                };
-                subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
-            }
+    fn decorate(self, dcx: &DiagCtxtInner) -> Diagnostic {
+        // We are at the `Diagnostic`/`DiagCtxtInner` level rather than the
+        // usual `DiagnosticBuilder`/`DiagCtxt` level, so we must construct
+        // `diag` in a lower-level fashion.
+        let mut diag = self.inner;
+        let msg = match self.note.status() {
+            BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline,
             // Avoid the needless newline when no backtrace has been captured,
             // the display impl should just be a single line.
-            _ => {
-                let inner = &self.inner;
-                let subdiag = DelayedAtWithoutNewline {
-                    span: inner.span.primary_span().unwrap_or(DUMMY_SP),
-                    emitted_at: inner.emitted_at.clone(),
-                    note: self.note,
-                };
-                subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
-            }
-        }
-
-        self.inner
+            _ => crate::fluent_generated::errors_delayed_at_without_newline,
+        };
+        diag.arg("emitted_at", diag.emitted_at.clone());
+        diag.arg("note", self.note);
+        let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls
+        diag.sub(Level::Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into());
+        diag
     }
 }
 
@@ -1745,9 +1748,9 @@ impl Level {
 }
 
 // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
-pub fn add_elided_lifetime_in_path_suggestion<E: EmissionGuarantee>(
+pub fn add_elided_lifetime_in_path_suggestion<G: EmissionGuarantee>(
     source_map: &SourceMap,
-    diag: &mut DiagnosticBuilder<'_, E>,
+    diag: &mut DiagnosticBuilder<'_, G>,
     n: usize,
     path_span: Span,
     incl_angl_brckt: bool,
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl
index 5a3303327db..1f0488130b2 100644
--- a/compiler/rustc_expand/messages.ftl
+++ b/compiler/rustc_expand/messages.ftl
@@ -22,6 +22,10 @@ expand_collapse_debuginfo_illegal =
 expand_count_repetition_misplaced =
     `count` can not be placed inside the inner-most repetition
 
+expand_custom_attribute_panicked =
+    custom attribute panicked
+    .help = message: {$message}
+
 expand_duplicate_matcher_binding = duplicate matcher binding
     .label = duplicate binding
     .label2 = previous binding
@@ -115,6 +119,10 @@ expand_only_one_argument =
 expand_only_one_word =
     must only be one word
 
+expand_proc_macro_derive_panicked =
+    proc-macro derive panicked
+    .help = message: {$message}
+
 expand_proc_macro_derive_tokens =
     proc-macro derive produced unparsable tokens
 
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index f602276aad2..1c8d18bec65 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -1176,6 +1176,8 @@ impl<'a> ExtCtxt<'a> {
         for (span, notes) in self.expansions.iter() {
             let mut db = self.dcx().create_note(errors::TraceMacro { span: *span });
             for note in notes {
+                // FIXME: make this translatable
+                #[allow(rustc::untranslatable_diagnostic)]
                 db.note(note.clone());
             }
             db.emit();
diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs
index 781186764fa..435135d1959 100644
--- a/compiler/rustc_expand/src/config.rs
+++ b/compiler/rustc_expand/src/config.rs
@@ -384,6 +384,7 @@ impl<'a> StripUnconfigured<'a> {
             );
 
             if attr.is_doc_comment() {
+                #[allow(rustc::untranslatable_diagnostic)]
                 err.help("`///` is for documentation comments. For a plain comment, use `//`.");
             }
 
diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs
index 929f3479466..fe901603c73 100644
--- a/compiler/rustc_expand/src/errors.rs
+++ b/compiler/rustc_expand/src/errors.rs
@@ -393,6 +393,36 @@ pub(crate) struct ProcMacroPanickedHelp {
 }
 
 #[derive(Diagnostic)]
+#[diag(expand_proc_macro_derive_panicked)]
+pub(crate) struct ProcMacroDerivePanicked {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub message: Option<ProcMacroDerivePanickedHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(expand_help)]
+pub(crate) struct ProcMacroDerivePanickedHelp {
+    pub message: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(expand_custom_attribute_panicked)]
+pub(crate) struct CustomAttributePanicked {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub message: Option<CustomAttributePanickedHelp>,
+}
+
+#[derive(Subdiagnostic)]
+#[help(expand_help)]
+pub(crate) struct CustomAttributePanickedHelp {
+    pub message: String,
+}
+
+#[derive(Diagnostic)]
 #[diag(expand_proc_macro_derive_tokens)]
 pub struct ProcMacroDeriveTokens {
     #[primary_span]
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index e7197c5768b..25af974d326 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, DiagCtxt, Diagnostic, DiagnosticBuilder, DiagnosticMessage};
+use rustc_errors::{Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticMessage};
 use rustc_parse::parser::{Parser, Recovery};
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::Ident;
@@ -285,7 +285,11 @@ pub(super) fn emit_frag_parse_err(
     e.emit();
 }
 
-pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) {
+pub(crate) fn annotate_err_with_kind(
+    err: &mut DiagnosticBuilder<'_>,
+    kind: AstFragmentKind,
+    span: Span,
+) {
     match kind {
         AstFragmentKind::Ty => {
             err.span_label(span, "this macro call doesn't expand to a type");
@@ -313,7 +317,7 @@ enum ExplainDocComment {
 
 pub(super) fn annotate_doc_comment(
     dcx: &DiagCtxt,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     sm: &SourceMap,
     span: Span,
 ) {
diff --git a/compiler/rustc_expand/src/proc_macro.rs b/compiler/rustc_expand/src/proc_macro.rs
index 2233cad2e63..23caf2f193a 100644
--- a/compiler/rustc_expand/src/proc_macro.rs
+++ b/compiler/rustc_expand/src/proc_macro.rs
@@ -93,11 +93,12 @@ impl base::AttrProcMacro for AttrProcMacro {
         let server = proc_macro_server::Rustc::new(ecx);
         self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err(
             |e| {
-                let mut err = ecx.dcx().struct_span_err(span, "custom attribute panicked");
-                if let Some(s) = e.as_str() {
-                    err.help(format!("message: {s}"));
-                }
-                err.emit()
+                ecx.dcx().emit_err(errors::CustomAttributePanicked {
+                    span,
+                    message: e.as_str().map(|message| errors::CustomAttributePanickedHelp {
+                        message: message.into(),
+                    }),
+                })
             },
         )
     }
@@ -146,11 +147,14 @@ impl MultiItemModifier for DeriveProcMacro {
             match self.client.run(&strategy, server, input, proc_macro_backtrace) {
                 Ok(stream) => stream,
                 Err(e) => {
-                    let mut err = ecx.dcx().struct_span_err(span, "proc-macro derive panicked");
-                    if let Some(s) = e.as_str() {
-                        err.help(format!("message: {s}"));
-                    }
-                    err.emit();
+                    ecx.dcx().emit_err({
+                        errors::ProcMacroDerivePanicked {
+                            span,
+                            message: e.as_str().map(|message| {
+                                errors::ProcMacroDerivePanickedHelp { message: message.into() }
+                            }),
+                        }
+                    });
                     return ExpandResult::Ready(vec![]);
                 }
             }
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 8f31b5801da..b80ecbc9c65 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -10,7 +10,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{ErrorGuaranteed, MultiSpan, PResult};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult};
 use rustc_parse::lexer::nfc_normalize;
 use rustc_parse::parse_stream_from_source_str;
 use rustc_session::parse::ParseSess;
@@ -509,13 +509,14 @@ impl server::FreeFunctions for Rustc<'_, '_> {
     }
 
     fn emit_diagnostic(&mut self, diagnostic: Diagnostic<Self::Span>) {
-        let mut diag =
-            rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message);
+        let message = rustc_errors::DiagnosticMessage::from(diagnostic.message);
+        let mut diag: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed> =
+            DiagnosticBuilder::new(&self.sess().dcx, diagnostic.level.to_internal(), message);
         diag.span(MultiSpan::from_spans(diagnostic.spans));
         for child in diagnostic.children {
             diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans));
         }
-        self.sess().dcx.emit_diagnostic(diag);
+        diag.emit();
     }
 }
 
diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs
index e6050327186..1eaab3d2aca 100644
--- a/compiler/rustc_hir/src/pat_util.rs
+++ b/compiler/rustc_hir/src/pat_util.rs
@@ -71,14 +71,21 @@ impl hir::Pat<'_> {
     /// Call `f` on every "binding" in a pattern, e.g., on `a` in
     /// `match foo() { Some(a) => (), None => () }`.
     ///
-    /// When encountering an or-pattern `p_0 | ... | p_n` only `p_0` will be visited.
+    /// When encountering an or-pattern `p_0 | ... | p_n` only the first non-never pattern will be
+    /// visited. If they're all never patterns we visit nothing, which is ok since a never pattern
+    /// cannot have bindings.
     pub fn each_binding_or_first(
         &self,
         f: &mut impl FnMut(hir::BindingAnnotation, HirId, Span, Ident),
     ) {
         self.walk(|p| match &p.kind {
             PatKind::Or(ps) => {
-                ps[0].each_binding_or_first(f);
+                for p in *ps {
+                    if !p.is_never_pattern() {
+                        p.each_binding_or_first(f);
+                        break;
+                    }
+                }
                 false
             }
             PatKind::Binding(bm, _, ident, _) => {
diff --git a/compiler/rustc_hir_analysis/src/astconv/bounds.rs b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
index 6940b4a5045..6d8a5bc0e90 100644
--- a/compiler/rustc_hir_analysis/src/astconv/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/bounds.rs
@@ -454,9 +454,9 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
                     //     for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
                     //     for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
                     let late_bound_in_projection_ty =
-                        tcx.collect_constrained_late_bound_regions(&projection_ty);
+                        tcx.collect_constrained_late_bound_regions(projection_ty);
                     let late_bound_in_term =
-                        tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(term));
+                        tcx.collect_referenced_late_bound_regions(trait_ref.rebind(term));
                     debug!(?late_bound_in_projection_ty);
                     debug!(?late_bound_in_term);
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
index ad34c31ef8f..214d9602968 100644
--- a/compiler/rustc_hir_analysis/src/astconv/errors.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
 };
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -371,7 +371,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     // FIXME(fmease): Heavily adapted from `rustc_hir_typeck::method::suggest`. Deduplicate.
     fn note_ambiguous_inherent_assoc_type(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         candidates: Vec<DefId>,
         span: Span,
     ) {
@@ -429,7 +429,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         let tcx = self.tcx();
 
         let adt_did = self_ty.ty_adt_def().map(|def| def.did());
-        let add_def_label = |err: &mut Diagnostic| {
+        let add_def_label = |err: &mut DiagnosticBuilder<'_>| {
             if let Some(did) = adt_did {
                 err.span_label(
                     tcx.def_span(did),
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
index 614e5f9d32b..b20326ae5e1 100644
--- a/compiler/rustc_hir_analysis/src/astconv/generics.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -6,7 +6,7 @@ use crate::astconv::{
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan,
+    codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -47,7 +47,7 @@ fn generic_arg_mismatch_err(
         }
     }
 
-    let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| {
+    let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut DiagnosticBuilder<'_>| {
         let suggestions = vec![
             (arg.span().shrink_to_lo(), String::from("{ ")),
             (arg.span().shrink_to_hi(), String::from(" }")),
diff --git a/compiler/rustc_hir_analysis/src/astconv/lint.rs b/compiler/rustc_hir_analysis/src/astconv/lint.rs
index cee7c84adb2..cee29b152e8 100644
--- a/compiler/rustc_hir_analysis/src/astconv/lint.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/lint.rs
@@ -1,5 +1,5 @@
 use rustc_ast::TraitObjectSyntax;
-use rustc_errors::{codes::*, Diagnostic, StashKey};
+use rustc_errors::{codes::*, DiagnosticBuilder, EmissionGuarantee, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_lint_defs::{builtin::BARE_TRAIT_OBJECTS, Applicability};
@@ -10,10 +10,10 @@ use super::AstConv;
 
 impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     /// Make sure that we are in the condition to suggest the blanket implementation.
-    pub(super) fn maybe_lint_blanket_trait_impl(
+    pub(super) fn maybe_lint_blanket_trait_impl<G: EmissionGuarantee>(
         &self,
         self_ty: &hir::Ty<'_>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_, G>,
     ) {
         let tcx = self.tcx();
         let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
@@ -75,7 +75,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     }
 
     /// Make sure that we are in the condition to suggest `impl Trait`.
-    fn maybe_lint_impl_trait(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) -> bool {
+    fn maybe_lint_impl_trait(
+        &self,
+        self_ty: &hir::Ty<'_>,
+        diag: &mut DiagnosticBuilder<'_>,
+    ) -> bool {
         let tcx = self.tcx();
         let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
         let (sig, generics, owner) = match tcx.hir_node_by_def_id(parent_id) {
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 3ccf78567ed..f374b955d9e 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -18,8 +18,8 @@ use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
-    FatalError, MultiSpan,
+    codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, FatalError,
+    MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@@ -1237,8 +1237,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 // trait reference.
                 let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
                     // A cycle error occurred, most likely.
-                    let guar = tcx.dcx().span_delayed_bug(span, "expected cycle error");
-                    return Err(guar);
+                    tcx.dcx().span_bug(span, "expected cycle error");
                 };
 
                 self.one_bound_for_assoc_item(
@@ -1425,17 +1424,16 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             vec![]
         };
 
-        let (impl_, (assoc_item, def_scope)) =
-            crate::traits::project::with_replaced_escaping_bound_vars(
-                infcx,
-                &mut universes,
-                self_ty,
-                |self_ty| {
-                    self.select_inherent_assoc_type_candidates(
-                        infcx, name, span, self_ty, param_env, candidates,
-                    )
-                },
-            )?;
+        let (impl_, (assoc_item, def_scope)) = crate::traits::with_replaced_escaping_bound_vars(
+            infcx,
+            &mut universes,
+            self_ty,
+            |self_ty| {
+                self.select_inherent_assoc_type_candidates(
+                    infcx, name, span, self_ty, param_env, candidates,
+                )
+            },
+        )?;
 
         self.check_assoc_ty(assoc_item, name, def_scope, block, span);
 
@@ -1725,7 +1723,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     pub fn prohibit_generics<'a>(
         &self,
         segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
-        extend: impl Fn(&mut Diagnostic),
+        extend: impl Fn(&mut DiagnosticBuilder<'_>),
     ) -> bool {
         let args = segments.clone().flat_map(|segment| segment.args().args);
 
@@ -2679,9 +2677,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         //     for<'a> fn(&'a String) -> &'a str <-- 'a is ok
         let inputs = bare_fn_ty.inputs();
         let late_bound_in_args =
-            tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned()));
+            tcx.collect_constrained_late_bound_regions(inputs.map_bound(|i| i.to_owned()));
         let output = bare_fn_ty.output();
-        let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
+        let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(output);
 
         self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
             struct_span_code_err!(
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index c3948b1b6bd..b945eed54d0 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -257,8 +257,7 @@ fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) {
     let item = tcx.hir().expect_item(def_id);
     let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
-        tcx.dcx().span_delayed_bug(item.span, "expected opaque item");
-        return;
+        tcx.dcx().span_bug(item.span, "expected opaque item");
     };
 
     // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
@@ -382,10 +381,10 @@ fn check_opaque_meets_bounds<'tcx>(
         Ok(()) => {}
         Err(ty_err) => {
             let ty_err = ty_err.to_string(tcx);
-            return Err(tcx.dcx().span_delayed_bug(
+            tcx.dcx().span_bug(
                 span,
                 format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
-            ));
+            );
         }
     }
 
@@ -1248,7 +1247,7 @@ fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) {
 fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) {
     // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate.
     // Here `idx` refers to the order of which the discriminant appears, and its index in `vs`
-    let report = |dis: Discr<'tcx>, idx, err: &mut Diagnostic| {
+    let report = |dis: Discr<'tcx>, idx, err: &mut DiagnosticBuilder<'_>| {
         let var = adt.variant(idx); // HIR for the duplicate discriminant
         let (span, display_discr) = match var.discr {
             ty::VariantDiscr::Explicit(discr_def_id) => {
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
index 1dd27f0cc53..435e251b130 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs
@@ -734,11 +734,12 @@ pub(super) fn collect_return_position_impl_trait_in_trait_tys<'tcx>(
                 remapped_types.insert(def_id, ty::EarlyBinder::bind(ty));
             }
             Err(err) => {
-                let reported = tcx.dcx().span_delayed_bug(
-                    return_span,
-                    format!("could not fully resolve: {ty} => {err:?}"),
-                );
-                remapped_types.insert(def_id, ty::EarlyBinder::bind(Ty::new_error(tcx, reported)));
+                // This code path is not reached in any tests, but may be
+                // reachable. If this is triggered, it should be converted to
+                // `span_delayed_bug` and the triggering case turned into a
+                // test.
+                tcx.dcx()
+                    .span_bug(return_span, format!("could not fully resolve: {ty} => {err:?}"));
             }
         }
     }
@@ -917,7 +918,13 @@ impl<'tcx> ty::FallibleTypeFolder<TyCtxt<'tcx>> for RemapHiddenTyRegions<'tcx> {
                         .with_note(format!("hidden type inferred to be `{}`", self.ty))
                         .emit()
                 }
-                _ => self.tcx.dcx().delayed_bug("should've been able to remap region"),
+                _ => {
+                    // This code path is not reached in any tests, but may be
+                    // reachable. If this is triggered, it should be converted
+                    // to `delayed_bug` and the triggering case turned into a
+                    // test.
+                    self.tcx.dcx().bug("should've been able to remap region");
+                }
             };
             return Err(guar);
         };
@@ -1276,9 +1283,10 @@ fn compare_number_of_generics<'tcx>(
     // inheriting the generics from will also have mismatched arguments, and
     // we'll report an error for that instead. Delay a bug for safety, though.
     if trait_.is_impl_trait_in_trait() {
-        return Err(tcx.dcx().delayed_bug(
-            "errors comparing numbers of generics of trait/impl functions were not emitted",
-        ));
+        // FIXME: no tests trigger this. If you find example code that does
+        // trigger this, please add it to the test suite.
+        tcx.dcx()
+            .bug("errors comparing numbers of generics of trait/impl functions were not emitted");
     }
 
     let matchings = [
diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
index 3d3b21eabb3..7bdbab4325c 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item/refine.rs
@@ -154,8 +154,10 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
         trait_m_sig.inputs_and_output,
     ));
     if !ocx.select_all_or_error().is_empty() {
-        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (selection)");
-        return;
+        // This code path is not reached in any tests, but may be reachable. If
+        // this is triggered, it should be converted to `delayed_bug` and the
+        // triggering case turned into a test.
+        tcx.dcx().bug("encountered errors when checking RPITIT refinement (selection)");
     }
     let outlives_env = OutlivesEnvironment::with_bounds(
         param_env,
@@ -163,13 +165,17 @@ pub(super) fn check_refining_return_position_impl_trait_in_trait<'tcx>(
     );
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
-        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (regions)");
-        return;
+        // This code path is not reached in any tests, but may be reachable. If
+        // this is triggered, it should be converted to `delayed_bug` and the
+        // triggering case turned into a test.
+        tcx.dcx().bug("encountered errors when checking RPITIT refinement (regions)");
     }
     // Resolve any lifetime variables that may have been introduced during normalization.
     let Ok((trait_bounds, impl_bounds)) = infcx.fully_resolve((trait_bounds, impl_bounds)) else {
-        tcx.dcx().delayed_bug("encountered errors when checking RPITIT refinement (resolution)");
-        return;
+        // This code path is not reached in any tests, but may be reachable. If
+        // this is triggered, it should be converted to `delayed_bug` and the
+        // triggering case turned into a test.
+        tcx.dcx().bug("encountered errors when checking RPITIT refinement (resolution)");
     };
 
     // For quicker lookup, use an `IndexSet` (we don't use one earlier because
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 903c98e8317..05fab60fd8d 100644
--- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -123,7 +123,12 @@ pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: LocalDefId) -
         | sym::variant_count
         | sym::is_val_statically_known
         | sym::ptr_mask
-        | sym::debug_assertions => hir::Unsafety::Normal,
+        | sym::debug_assertions
+        | sym::fadd_algebraic
+        | sym::fsub_algebraic
+        | sym::fmul_algebraic
+        | sym::fdiv_algebraic
+        | sym::frem_algebraic => hir::Unsafety::Normal,
         _ => hir::Unsafety::Unsafe,
     };
 
@@ -405,6 +410,11 @@ pub fn check_intrinsic_type(
             sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
                 (1, 0, vec![param(0), param(0)], param(0))
             }
+            sym::fadd_algebraic
+            | sym::fsub_algebraic
+            | sym::fmul_algebraic
+            | sym::fdiv_algebraic
+            | sym::frem_algebraic => (1, 0, vec![param(0), param(0)], param(0)),
             sym::float_to_int_unchecked => (2, 0, vec![param(0)], param(1)),
 
             sym::assume => (0, 1, vec![tcx.types.bool], Ty::new_unit(tcx)),
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
index b9052672a26..36a92a4cf7c 100644
--- a/compiler/rustc_hir_analysis/src/check/mod.rs
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -78,7 +78,7 @@ use std::num::NonZero;
 
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_errors::ErrorGuaranteed;
-use rustc_errors::{pluralize, struct_span_code_err, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{pluralize, struct_span_code_err, DiagnosticBuilder};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::Visitor;
 use rustc_index::bit_set::BitSet;
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index e7506cee60e..f03d0f8a885 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1087,14 +1087,8 @@ fn check_type_defn<'tcx>(
                 packed && {
                     let ty = tcx.type_of(variant.tail().did).instantiate_identity();
                     let ty = tcx.erase_regions(ty);
-                    if ty.has_infer() {
-                        tcx.dcx()
-                            .span_delayed_bug(item.span, format!("inference variables in {ty:?}"));
-                        // Just treat unresolved type expression as if it needs drop.
-                        true
-                    } else {
-                        ty.needs_drop(tcx, tcx.param_env(item.owner_id))
-                    }
+                    assert!(!ty.has_infer());
+                    ty.needs_drop(tcx, tcx.param_env(item.owner_id))
                 }
             };
             // All fields (except for possibly the last) should be sized.
diff --git a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 4823472cf96..32f31201254 100644
--- a/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -144,7 +144,7 @@ impl<'tcx> InherentCollect<'tcx> {
         let id = id.owner_id.def_id;
         let item_span = self.tcx.def_span(id);
         let self_ty = self.tcx.type_of(id).instantiate_identity();
-        let self_ty = peel_off_weak_aliases(self.tcx, self_ty);
+        let self_ty = self.tcx.peel_off_weak_alias_tys(self_ty);
         match *self_ty.kind() {
             ty::Adt(def, _) => self.check_def_id(id, self_ty, def.did()),
             ty::Foreign(did) => self.check_def_id(id, self_ty, did),
@@ -186,30 +186,3 @@ impl<'tcx> InherentCollect<'tcx> {
         }
     }
 }
-
-/// Peel off all weak alias types in this type until there are none left.
-///
-/// <div class="warning">
-///
-/// This assumes that `ty` gets normalized later and that any overflows occurring
-/// during said normalization get reported.
-///
-/// </div>
-fn peel_off_weak_aliases<'tcx>(tcx: TyCtxt<'tcx>, mut ty: Ty<'tcx>) -> Ty<'tcx> {
-    let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
-
-    let limit = tcx.recursion_limit();
-    let mut depth = 0;
-
-    while let ty::Alias(ty::Weak, alias) = ty.kind() {
-        if !limit.value_within_limit(depth) {
-            let guar = tcx.dcx().delayed_bug("overflow expanding weak alias type");
-            return Ty::new_error(tcx, guar);
-        }
-
-        ty = tcx.type_of(alias.def_id).instantiate(tcx, alias.args);
-        depth += 1;
-    }
-
-    ty
-}
diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 45641be52d2..07bbaa1926e 100644
--- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -1,20 +1,12 @@
 //! Orphan checker: every impl either implements a trait defined in this
 //! crate or pertains to a type defined in this crate.
 
-use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{DelayDm, ErrorGuaranteed};
+use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
-use rustc_middle::ty::util::CheckRegions;
-use rustc_middle::ty::GenericArgs;
-use rustc_middle::ty::{
-    self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
-    TypeVisitor,
-};
-use rustc_session::lint;
-use rustc_span::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::{self, AliasKind, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
 use rustc_trait_selection::traits;
-use std::ops::ControlFlow;
 
 use crate::errors;
 
@@ -26,30 +18,17 @@ pub(crate) fn orphan_check_impl(
     let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
     trait_ref.error_reported()?;
 
-    let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
-    if tcx.trait_is_auto(trait_ref.def_id) {
-        lint_auto_trait_impl(tcx, trait_ref, impl_def_id);
-    }
-
-    ret
-}
-
-fn do_orphan_check_impl<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-    def_id: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
     let trait_def_id = trait_ref.def_id;
 
-    match traits::orphan_check(tcx, def_id.to_def_id()) {
+    match traits::orphan_check(tcx, impl_def_id.to_def_id()) {
         Ok(()) => {}
         Err(err) => {
-            let item = tcx.hir().expect_item(def_id);
+            let item = tcx.hir().expect_item(impl_def_id);
             let hir::ItemKind::Impl(impl_) = item.kind else {
-                bug!("{:?} is not an impl: {:?}", def_id, item);
+                bug!("{:?} is not an impl: {:?}", impl_def_id, item);
             };
             let tr = impl_.of_trait.as_ref().unwrap();
-            let sp = tcx.def_span(def_id);
+            let sp = tcx.def_span(impl_def_id);
 
             emit_orphan_check_error(
                 tcx,
@@ -193,7 +172,7 @@ fn do_orphan_check_impl<'tcx>(
             // impl<T> AutoTrait for T {}
             // impl<T: ?Sized> AutoTrait for T {}
             ty::Param(..) => (
-                if self_ty.is_sized(tcx, tcx.param_env(def_id)) {
+                if self_ty.is_sized(tcx, tcx.param_env(impl_def_id)) {
                     LocalImpl::Allow
                 } else {
                     LocalImpl::Disallow { problematic_kind: "generic type" }
@@ -250,7 +229,7 @@ fn do_orphan_check_impl<'tcx>(
             | ty::Bound(..)
             | ty::Placeholder(..)
             | ty::Infer(..) => {
-                let sp = tcx.def_span(def_id);
+                let sp = tcx.def_span(impl_def_id);
                 span_bug!(sp, "weird self type for autotrait impl")
             }
 
@@ -262,7 +241,7 @@ fn do_orphan_check_impl<'tcx>(
                 LocalImpl::Allow => {}
                 LocalImpl::Disallow { problematic_kind } => {
                     return Err(tcx.dcx().emit_err(errors::TraitsWithDefaultImpl {
-                        span: tcx.def_span(def_id),
+                        span: tcx.def_span(impl_def_id),
                         traits: tcx.def_path_str(trait_def_id),
                         problematic_kind,
                         self_ty,
@@ -274,13 +253,13 @@ fn do_orphan_check_impl<'tcx>(
                 NonlocalImpl::Allow => {}
                 NonlocalImpl::DisallowBecauseNonlocal => {
                     return Err(tcx.dcx().emit_err(errors::CrossCrateTraitsDefined {
-                        span: tcx.def_span(def_id),
+                        span: tcx.def_span(impl_def_id),
                         traits: tcx.def_path_str(trait_def_id),
                     }));
                 }
                 NonlocalImpl::DisallowOther => {
                     return Err(tcx.dcx().emit_err(errors::CrossCrateTraits {
-                        span: tcx.def_span(def_id),
+                        span: tcx.def_span(impl_def_id),
                         traits: tcx.def_path_str(trait_def_id),
                         self_ty,
                     }));
@@ -445,146 +424,3 @@ fn emit_orphan_check_error<'tcx>(
         }
     })
 }
-
-/// Lint impls of auto traits if they are likely to have
-/// unsound or surprising effects on auto impls.
-fn lint_auto_trait_impl<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    trait_ref: ty::TraitRef<'tcx>,
-    impl_def_id: LocalDefId,
-) {
-    if trait_ref.args.len() != 1 {
-        tcx.dcx().span_delayed_bug(
-            tcx.def_span(impl_def_id),
-            "auto traits cannot have generic parameters",
-        );
-        return;
-    }
-    let self_ty = trait_ref.self_ty();
-    let (self_type_did, args) = match self_ty.kind() {
-        ty::Adt(def, args) => (def.did(), args),
-        _ => {
-            // FIXME: should also lint for stuff like `&i32` but
-            // considering that auto traits are unstable, that
-            // isn't too important for now as this only affects
-            // crates using `nightly`, and std.
-            return;
-        }
-    };
-
-    // Impls which completely cover a given root type are fine as they
-    // disable auto impls entirely. So only lint if the args
-    // are not a permutation of the identity args.
-    let Err(arg) = tcx.uses_unique_generic_params(args, CheckRegions::No) else {
-        // ok
-        return;
-    };
-
-    // Ideally:
-    //
-    // - compute the requirements for the auto impl candidate
-    // - check whether these are implied by the non covering impls
-    // - if not, emit the lint
-    //
-    // What we do here is a bit simpler:
-    //
-    // - badly check if an auto impl candidate definitely does not apply
-    //   for the given simplified type
-    // - if so, do not lint
-    if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) {
-        // ok
-        return;
-    }
-
-    tcx.node_span_lint(
-        lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
-        tcx.local_def_id_to_hir_id(impl_def_id),
-        tcx.def_span(impl_def_id),
-        DelayDm(|| {
-            format!(
-                "cross-crate traits with a default impl, like `{}`, \
-                         should not be specialized",
-                tcx.def_path_str(trait_ref.def_id),
-            )
-        }),
-        |lint| {
-            let item_span = tcx.def_span(self_type_did);
-            let self_descr = tcx.def_descr(self_type_did);
-            match arg {
-                ty::util::NotUniqueParam::DuplicateParam(arg) => {
-                    lint.note(format!("`{arg}` is mentioned multiple times"));
-                }
-                ty::util::NotUniqueParam::NotParam(arg) => {
-                    lint.note(format!("`{arg}` is not a generic parameter"));
-                }
-            }
-            lint.span_note(
-                item_span,
-                format!(
-                    "try using the same sequence of generic parameters as the {self_descr} definition",
-                ),
-            );
-        },
-    );
-}
-
-fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool {
-    struct DisableAutoTraitVisitor<'tcx> {
-        tcx: TyCtxt<'tcx>,
-        trait_def_id: DefId,
-        self_ty_root: Ty<'tcx>,
-        seen: FxHashSet<DefId>,
-    }
-
-    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for DisableAutoTraitVisitor<'tcx> {
-        type BreakTy = ();
-        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-            let tcx = self.tcx;
-            if ty != self.self_ty_root {
-                for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, ty) {
-                    match tcx.impl_polarity(impl_def_id) {
-                        ImplPolarity::Negative => return ControlFlow::Break(()),
-                        ImplPolarity::Reservation => {}
-                        // FIXME(@lcnr): That's probably not good enough, idk
-                        //
-                        // We might just want to take the rustdoc code and somehow avoid
-                        // explicit impls for `Self`.
-                        ImplPolarity::Positive => return ControlFlow::Continue(()),
-                    }
-                }
-            }
-
-            match ty.kind() {
-                ty::Adt(def, args) if def.is_phantom_data() => args.visit_with(self),
-                ty::Adt(def, args) => {
-                    // @lcnr: This is the only place where cycles can happen. We avoid this
-                    // by only visiting each `DefId` once.
-                    //
-                    // This will be is incorrect in subtle cases, but I don't care :)
-                    if self.seen.insert(def.did()) {
-                        for ty in def.all_fields().map(|field| field.ty(tcx, args)) {
-                            ty.visit_with(self)?;
-                        }
-                    }
-
-                    ControlFlow::Continue(())
-                }
-                _ => ty.super_visit_with(self),
-            }
-        }
-    }
-
-    let self_ty_root = match self_ty.kind() {
-        ty::Adt(def, _) => Ty::new_adt(tcx, *def, GenericArgs::identity_for_item(tcx, def.did())),
-        _ => unimplemented!("unexpected self ty {:?}", self_ty),
-    };
-
-    self_ty_root
-        .visit_with(&mut DisableAutoTraitVisitor {
-            tcx,
-            self_ty_root,
-            trait_def_id,
-            seen: FxHashSet::default(),
-        })
-        .is_break()
-}
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index a5ef1490bce..6a42fdb1079 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -520,7 +520,7 @@ fn get_new_lifetime_name<'tcx>(
     generics: &hir::Generics<'tcx>,
 ) -> String {
     let existing_lifetimes = tcx
-        .collect_referenced_late_bound_regions(&poly_trait_ref)
+        .collect_referenced_late_bound_regions(poly_trait_ref)
         .into_iter()
         .filter_map(|lt| {
             if let ty::BoundRegionKind::BrNamed(_, name) = lt {
@@ -1630,7 +1630,7 @@ fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>(
 #[instrument(level = "debug", skip(tcx))]
 fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
     let mut result = tcx.explicit_predicates_of(def_id);
-    debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,);
+    debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result);
     let inferred_outlives = tcx.inferred_outlives_of(def_id);
     if !inferred_outlives.is_empty() {
         debug!(
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
index 9cc6c16c126..410a069f956 100644
--- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -315,7 +315,7 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
 
             if is_host_effect {
                 if let Some(idx) = host_effect_index {
-                    tcx.dcx().span_delayed_bug(
+                    tcx.dcx().span_bug(
                         param.span,
                         format!("parent also has host effect param? index: {idx}, def: {def_id:?}"),
                     );
diff --git a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
index 325a0ee9a18..efde4e11c79 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1331,7 +1331,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             }
         }
 
-        self.tcx.dcx().span_delayed_bug(
+        self.tcx.dcx().span_bug(
             lifetime_ref.ident.span,
             format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
         );
@@ -1465,10 +1465,9 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
             }
         }
 
-        self.tcx.dcx().span_delayed_bug(
-            self.tcx.hir().span(hir_id),
-            format!("could not resolve {param_def_id:?}"),
-        );
+        self.tcx
+            .dcx()
+            .span_bug(self.tcx.hir().span(hir_id), format!("could not resolve {param_def_id:?}"));
     }
 
     #[instrument(level = "debug", skip(self))]
diff --git a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 4ce43bb4887..b8de2e46934 100644
--- a/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -1,8 +1,8 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::Span;
+use rustc_type_ir::fold::TypeFoldable;
 use std::ops::ControlFlow;
 
 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
@@ -33,62 +33,47 @@ pub fn parameters_for_impl<'tcx>(
     impl_trait_ref: Option<ty::TraitRef<'tcx>>,
 ) -> FxHashSet<Parameter> {
     let vec = match impl_trait_ref {
-        Some(tr) => parameters_for(tcx, &tr, false),
-        None => parameters_for(tcx, &impl_self_ty, false),
+        Some(tr) => parameters_for(tcx, tr, false),
+        None => parameters_for(tcx, impl_self_ty, false),
     };
     vec.into_iter().collect()
 }
 
 /// If `include_nonconstraining` is false, returns the list of parameters that are
-/// constrained by `t` - i.e., the value of each parameter in the list is
-/// uniquely determined by `t` (see RFC 447). If it is true, return the list
-/// of parameters whose values are needed in order to constrain `ty` - these
+/// constrained by `value` - i.e., the value of each parameter in the list is
+/// uniquely determined by `value` (see RFC 447). If it is true, return the list
+/// of parameters whose values are needed in order to constrain `value` - these
 /// differ, with the latter being a superset, in the presence of projections.
 pub fn parameters_for<'tcx>(
     tcx: TyCtxt<'tcx>,
-    t: &impl TypeVisitable<TyCtxt<'tcx>>,
+    value: impl TypeFoldable<TyCtxt<'tcx>>,
     include_nonconstraining: bool,
 ) -> Vec<Parameter> {
-    let mut collector =
-        ParameterCollector { tcx, parameters: vec![], include_nonconstraining, depth: 0 };
-    t.visit_with(&mut collector);
+    let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining };
+    let value = if !include_nonconstraining { tcx.expand_weak_alias_tys(value) } else { value };
+    value.visit_with(&mut collector);
     collector.parameters
 }
 
-struct ParameterCollector<'tcx> {
-    tcx: TyCtxt<'tcx>,
+struct ParameterCollector {
     parameters: Vec<Parameter>,
     include_nonconstraining: bool,
-    depth: usize,
 }
 
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector<'tcx> {
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ParameterCollector {
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
         match *t.kind() {
+            // Projections are not injective in general.
             ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _)
                 if !self.include_nonconstraining =>
             {
-                // Projections are not injective in general.
                 return ControlFlow::Continue(());
             }
-            ty::Alias(ty::Weak, alias) if !self.include_nonconstraining => {
-                if !self.tcx.recursion_limit().value_within_limit(self.depth) {
-                    // Other constituent types may still constrain some generic params, consider
-                    // `<T> (Overflow, T)` for example. Therefore we want to continue instead of
-                    // breaking. Only affects diagnostics.
-                    return ControlFlow::Continue(());
-                }
-                self.depth += 1;
-                return ensure_sufficient_stack(|| {
-                    self.tcx
-                        .type_of(alias.def_id)
-                        .instantiate(self.tcx, alias.args)
-                        .visit_with(self)
-                });
-            }
-            ty::Param(data) => {
-                self.parameters.push(Parameter::from(data));
+            // All weak alias types should've been expanded beforehand.
+            ty::Alias(ty::Weak, _) if !self.include_nonconstraining => {
+                bug!("unexpected weak alias type")
             }
+            ty::Param(param) => self.parameters.push(Parameter::from(param)),
             _ => {}
         }
 
@@ -224,12 +209,12 @@ pub fn setup_constraining_predicates<'tcx>(
                 //     `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output`
                 // Then the projection only applies if `T` is known, but it still
                 // does not determine `U`.
-                let inputs = parameters_for(tcx, &projection.projection_ty, true);
+                let inputs = parameters_for(tcx, projection.projection_ty, true);
                 let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p));
                 if !relies_only_on_inputs {
                     continue;
                 }
-                input_parameters.extend(parameters_for(tcx, &projection.term, false));
+                input_parameters.extend(parameters_for(tcx, projection.term, false));
             } else {
                 continue;
             }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index b4cec1d9882..9d7866fe3e0 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -111,7 +111,7 @@ fn enforce_impl_params_are_constrained(
             match item.kind {
                 ty::AssocKind::Type => {
                     if item.defaultness(tcx).has_value() {
-                        cgp::parameters_for(tcx, &tcx.type_of(def_id).instantiate_identity(), true)
+                        cgp::parameters_for(tcx, tcx.type_of(def_id).instantiate_identity(), true)
                     } else {
                         vec![]
                     }
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index bd4fce81377..be1be1a1354 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -133,7 +133,7 @@ fn check_always_applicable(
 
     res = res.and(check_constness(tcx, impl1_def_id, impl2_node, span));
     res = res.and(check_static_lifetimes(tcx, &parent_args, span));
-    res = res.and(check_duplicate_params(tcx, impl1_args, &parent_args, span));
+    res = res.and(check_duplicate_params(tcx, impl1_args, parent_args, span));
     res = res.and(check_predicates(tcx, impl1_def_id, impl1_args, impl2_node, impl2_args, span));
 
     res
@@ -266,15 +266,15 @@ fn unconstrained_parent_impl_args<'tcx>(
                 continue;
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projection_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, projection_ty, true));
 
-            for param in cgp::parameters_for(tcx, &projected_ty, false) {
+            for param in cgp::parameters_for(tcx, projected_ty, false) {
                 if !unconstrained_parameters.contains(&param) {
                     constrained_params.insert(param.0);
                 }
             }
 
-            unconstrained_parameters.extend(cgp::parameters_for(tcx, &projected_ty, true));
+            unconstrained_parameters.extend(cgp::parameters_for(tcx, projected_ty, true));
         }
     }
 
@@ -309,7 +309,7 @@ fn unconstrained_parent_impl_args<'tcx>(
 fn check_duplicate_params<'tcx>(
     tcx: TyCtxt<'tcx>,
     impl1_args: GenericArgsRef<'tcx>,
-    parent_args: &Vec<GenericArg<'tcx>>,
+    parent_args: Vec<GenericArg<'tcx>>,
     span: Span,
 ) -> Result<(), ErrorGuaranteed> {
     let mut base_params = cgp::parameters_for(tcx, parent_args, true);
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index e53f922ad10..7cb103626da 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -198,6 +198,17 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
         collect::test_opaque_hidden_types(tcx)?;
     }
 
+    // Make sure we evaluate all static and (non-associated) const items, even if unused.
+    // If any of these fail to evaluate, we do not want this crate to pass compilation.
+    tcx.hir().par_body_owners(|item_def_id| {
+        let def_kind = tcx.def_kind(item_def_id);
+        match def_kind {
+            DefKind::Static(_) => tcx.ensure().eval_static_initializer(item_def_id),
+            DefKind::Const => tcx.ensure().const_eval_poly(item_def_id.into()),
+            _ => (),
+        }
+    });
+
     // Freeze definitions as we don't add new ones at this point. This improves performance by
     // allowing lock-free access to them.
     tcx.untracked().definitions.freeze();
diff --git a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index f9d57d402b1..8e0c2ea5ca7 100644
--- a/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -1,5 +1,5 @@
 use crate::structured_errors::StructuredDiagnostic;
-use rustc_errors::{codes::*, pluralize, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan};
+use rustc_errors::{codes::*, pluralize, Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
 use rustc_session::Session;
@@ -525,7 +525,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
-    fn notify(&self, err: &mut Diagnostic) {
+    fn notify(&self, err: &mut DiagnosticBuilder<'_>) {
         let (quantifier, bound) = self.get_quantifier_and_bound();
         let provided_args = self.num_provided_args();
 
@@ -577,7 +577,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest(&self, err: &mut Diagnostic) {
+    fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
         debug!(
             "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
             self.num_provided_args(),
@@ -605,7 +605,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String>;
     /// ```
-    fn suggest_adding_args(&self, err: &mut Diagnostic) {
+    fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
         if self.gen_args.parenthesized != hir::GenericArgsParentheses::No {
             return;
         }
@@ -624,7 +624,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) {
+    fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) {
         debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
         let num_missing_args = self.num_missing_lifetime_args();
         let num_params_to_take = num_missing_args;
@@ -678,7 +678,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) {
+    fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) {
         let num_missing_args = self.num_missing_type_or_const_args();
         let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
 
@@ -738,7 +738,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```compile_fail
     /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
     /// ```
-    fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
+    fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut DiagnosticBuilder<'_>) {
         let trait_ = match self.tcx.trait_of_item(self.def_id) {
             Some(def_id) => def_id,
             None => return,
@@ -794,7 +794,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
 
     fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         qpath: &'tcx hir::QPath<'tcx>,
         msg: String,
         num_assoc_fn_excess_args: usize,
@@ -827,7 +827,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
 
     fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_def_id: DefId,
         expr: &'tcx hir::Expr<'tcx>,
         msg: String,
@@ -881,7 +881,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String, String, String, String>;
     /// ```
-    fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
+    fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
         let num_provided_lt_args = self.num_provided_lifetime_args();
         let num_provided_type_const_args = self.num_provided_type_or_const_args();
         let unbound_types = self.get_unbound_associated_types();
@@ -899,7 +899,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         let provided_args_matches_unbound_traits =
             unbound_types.len() == num_redundant_type_or_const_args;
 
-        let remove_lifetime_args = |err: &mut Diagnostic| {
+        let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| {
             let mut lt_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -940,7 +940,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             );
         };
 
-        let remove_type_or_const_args = |err: &mut Diagnostic| {
+        let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| {
             let mut gen_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -1037,7 +1037,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Builds the `type defined here` message.
-    fn show_definition(&self, err: &mut Diagnostic) {
+    fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
         let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
             if self.tcx.sess.source_map().is_span_accessible(def_span) {
                 def_span.into()
@@ -1088,7 +1088,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Add note if `impl Trait` is explicitly specified.
-    fn note_synth_provided(&self, err: &mut Diagnostic) {
+    fn note_synth_provided(&self, err: &mut DiagnosticBuilder<'_>) {
         if !self.is_synth_provided() {
             return;
         }
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 18adf37d08c..cb131f1d166 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -1,6 +1,6 @@
 use crate::coercion::{AsCoercionSite, CoerceMany};
 use crate::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir::{
     self as hir,
     def::{CtorOf, DefKind, Res},
@@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn explain_never_type_coerced_to_unit(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         arm: &hir::Arm<'tcx>,
         arm_ty: Ty<'tcx>,
         prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
@@ -209,7 +209,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_removing_semicolon_for_coerce(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         arm_ty: Ty<'tcx>,
         prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
@@ -303,7 +303,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// `if let PAT = EXPR {}` expressions that could be turned into `let PAT = EXPR;`.
     fn explain_if_expr(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ret_reason: Option<(Span, String)>,
         if_span: Span,
         cond_expr: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs
index 27614634c6b..69bcb6b8c15 100644
--- a/compiler/rustc_hir_typeck/src/callee.rs
+++ b/compiler/rustc_hir_typeck/src/callee.rs
@@ -4,7 +4,7 @@ use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 
 use crate::errors;
 use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, StashKey};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, Namespace, Res};
 use rustc_hir::def_id::DefId;
@@ -347,7 +347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// likely intention is to call the closure, suggest `(||{})()`. (#55851)
     fn identify_bad_closure_def_and_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         hir_id: hir::HirId,
         callee_node: &hir::ExprKind<'_>,
         callee_span: Span,
@@ -410,7 +410,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// likely intention is to create an array containing tuples.
     fn maybe_suggest_bad_array_definition(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         call_expr: &'tcx hir::Expr<'tcx>,
         callee_expr: &'tcx hir::Expr<'tcx>,
     ) -> bool {
@@ -601,7 +601,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// and suggesting the fix if the method probe is successful.
     fn suggest_call_as_method(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_, ()>,
         segment: &'tcx hir::PathSegment<'tcx>,
         arg_exprs: &'tcx [hir::Expr<'tcx>],
         call_expr: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 042dd576c5b..b662d23c271 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -33,7 +33,7 @@ use super::FnCtxt;
 use crate::errors;
 use crate::type_error_struct;
 use hir::ExprKind;
-use rustc_errors::{codes::*, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{codes::*, Applicability, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_macros::{TypeFoldable, TypeVisitable};
 use rustc_middle::mir::Mutability;
@@ -139,10 +139,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             | ty::Never
             | ty::Dynamic(_, _, ty::DynStar)
             | ty::Error(_) => {
-                let guar = self
-                    .dcx()
-                    .span_delayed_bug(span, format!("`{t:?}` should be sized but is not?"));
-                return Err(guar);
+                self.dcx().span_bug(span, format!("`{t:?}` should be sized but is not?"));
             }
         })
     }
@@ -983,7 +980,11 @@ impl<'a, 'tcx> CastCheck<'tcx> {
 
     /// Attempt to suggest using `.is_empty` when trying to cast from a
     /// collection type to a boolean.
-    fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diagnostic) {
+    fn try_suggest_collection_to_bool(
+        &self,
+        fcx: &FnCtxt<'a, 'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
         if self.cast_ty.is_bool() {
             let derefed = fcx
                 .autoderef(self.expr_span, self.expr_ty)
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index 6bab8f75d24..9f6175eac13 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -36,9 +36,7 @@
 //! ```
 
 use crate::FnCtxt;
-use rustc_errors::{
-    codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
-};
+use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -1439,7 +1437,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         &mut self,
         fcx: &FnCtxt<'a, 'tcx>,
         cause: &ObligationCause<'tcx>,
-        augment_error: impl FnOnce(&mut Diagnostic),
+        augment_error: impl FnOnce(&mut DiagnosticBuilder<'_>),
         label_unit_as_expected: bool,
     ) {
         self.coerce_inner(
@@ -1462,7 +1460,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         cause: &ObligationCause<'tcx>,
         expression: Option<&'tcx hir::Expr<'tcx>>,
         mut expression_ty: Ty<'tcx>,
-        augment_error: impl FnOnce(&mut Diagnostic),
+        augment_error: impl FnOnce(&mut DiagnosticBuilder<'_>),
         label_expression_as_expected: bool,
     ) {
         // Incorporate whatever type inference information we have
@@ -1673,7 +1671,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
 
     fn note_unreachable_loop_return(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         tcx: TyCtxt<'tcx>,
         expr: &hir::Expr<'tcx>,
         ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index fdb2cb69ee7..98e3fbb8b11 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,6 +1,6 @@
 use crate::FnCtxt;
 use rustc_errors::MultiSpan;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::intravisit::Visitor;
@@ -19,7 +19,7 @@ use super::method::probe;
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn emit_type_mismatch_suggestions(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
@@ -70,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn emit_coerce_suggestions(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
@@ -280,7 +280,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// with some expectation given by `source`.
     pub fn note_source_of_type_mismatch_constraint(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         source: TypeMismatchSource<'tcx>,
     ) -> bool {
@@ -550,7 +550,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     // expected type.
     pub fn annotate_loop_expected_due_to_inference(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         error: Option<TypeError<'tcx>>,
     ) {
@@ -673,7 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn annotate_expected_due_to_let_ty(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         error: Option<TypeError<'tcx>>,
     ) {
@@ -782,7 +782,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn annotate_alternative_method_deref(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         error: Option<TypeError<'tcx>>,
     ) {
@@ -1029,7 +1029,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn explain_self_literal(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -1082,7 +1082,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn note_wrong_return_ty_due_to_generic_arg(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         checked_ty: Ty<'tcx>,
     ) {
diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs
index 1af0b75bd23..f609d0f7e8f 100644
--- a/compiler/rustc_hir_typeck/src/errors.rs
+++ b/compiler/rustc_hir_typeck/src/errors.rs
@@ -3,8 +3,8 @@ use std::borrow::Cow;
 
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg,
-    MultiSpan, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, Applicability, DiagnosticArgValue, DiagnosticBuilder,
+    EmissionGuarantee, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
 use rustc_middle::ty::Ty;
@@ -195,7 +195,11 @@ pub struct TypeMismatchFruTypo {
 }
 
 impl AddToDiagnostic for TypeMismatchFruTypo {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.arg("expr", self.expr.as_deref().unwrap_or("NONE"));
 
         // Only explain that `a ..b` is a range if it's split up
@@ -370,7 +374,11 @@ pub struct RemoveSemiForCoerce {
 }
 
 impl AddToDiagnostic for RemoveSemiForCoerce {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let mut multispan: MultiSpan = self.semi.into();
         multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr);
         multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret);
@@ -541,8 +549,12 @@ pub enum CastUnknownPointerSub {
     From(Span),
 }
 
-impl AddToDiagnostic for CastUnknownPointerSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         match self {
             CastUnknownPointerSub::To(span) => {
                 let msg = f(diag, crate::fluent_generated::hir_typeck_label_to);
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index a946d59ff2b..89cc46dc5ab 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -26,8 +26,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_data_structures::unord::UnordMap;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic,
-    DiagnosticBuilder, ErrorGuaranteed, StashKey,
+    codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, DiagnosticBuilder,
+    ErrorGuaranteed, StashKey,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -69,23 +69,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected_ty: Ty<'tcx>,
-        extend_err: impl FnOnce(&mut Diagnostic),
+        extend_err: impl FnOnce(&mut DiagnosticBuilder<'_>),
     ) -> Ty<'tcx> {
         let mut ty = self.check_expr_with_expectation(expr, ExpectHasType(expected_ty));
 
         // While we don't allow *arbitrary* coercions here, we *do* allow
         // coercions from ! to `expected`.
         if ty.is_never() {
-            if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
-                let reported = self.dcx().span_delayed_bug(
-                    expr.span,
-                    "expression with never type wound up being adjusted",
-                );
-                return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
-                    target.to_owned()
-                } else {
-                    Ty::new_error(self.tcx(), reported)
-                };
+            if let Some(_) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
+                self.dcx()
+                    .span_bug(expr.span, "expression with never type wound up being adjusted");
             }
 
             let adj_ty = self.next_ty_var(TypeVariableOrigin {
@@ -958,7 +951,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         lhs: &'tcx hir::Expr<'tcx>,
         code: ErrCode,
         op_span: Span,
-        adjust_err: impl FnOnce(&mut Diagnostic),
+        adjust_err: impl FnOnce(&mut DiagnosticBuilder<'_>),
     ) {
         if lhs.is_syntactic_place_expr() {
             return;
@@ -1223,7 +1216,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace);
 
-        let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| {
+        let suggest_deref_binop = |err: &mut DiagnosticBuilder<'_>, rhs_ty: Ty<'tcx>| {
             if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
                 // Can only assign if the type is sized, so if `DerefMut` yields a type that is
                 // unsized, do not suggest dereferencing it.
@@ -1322,10 +1315,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // the LUB of the breaks (possibly ! if none); else, it
         // is nil. This makes sense because infinite loops
         // (which would have type !) are only possible iff we
-        // permit break with a value [1].
+        // permit break with a value.
         if ctxt.coerce.is_none() && !ctxt.may_break {
-            // [1]
-            self.dcx().span_delayed_bug(body.span, "no coercion, but loop may not break");
+            self.dcx().span_bug(body.span, "no coercion, but loop may not break");
         }
         ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| Ty::new_unit(self.tcx))
     }
@@ -2008,7 +2000,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         last_expr_field: &hir::ExprField<'tcx>,
         variant: &ty::VariantDef,
         args: GenericArgsRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         // I don't use 'is_range_literal' because only double-sided, half-open ranges count.
         if let ExprKind::Struct(QPath::LangItem(LangItem::Range, ..), [range_start, range_end], _) =
@@ -2524,7 +2516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_await_on_field_access(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         field_ident: Ident,
         base: &'tcx hir::Expr<'tcx>,
         ty: Ty<'tcx>,
@@ -2723,7 +2715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit()
     }
 
-    fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) {
+    fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) {
         let generics = self.tcx.generics_of(self.body_id);
         let generic_param = generics.type_param(&param, self.tcx);
         if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
@@ -2742,7 +2734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn maybe_suggest_array_indexing(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         base: &hir::Expr<'_>,
         field: Ident,
@@ -2766,7 +2758,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_first_deref_field(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         base: &hir::Expr<'_>,
         field: Ident,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index ce8b62d19b4..986af2f5c9e 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -5,7 +5,7 @@ use crate::rvalue_scopes;
 use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LoweredTy};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -1020,7 +1020,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn note_internal_mutation_in_method(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected: Option<Ty<'tcx>>,
         found: Ty<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index d8d3d45dd40..2d9ec9f6bab 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -13,7 +13,7 @@ use itertools::Itertools;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{
-    codes::*, pluralize, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey,
+    codes::*, pluralize, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -740,8 +740,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         });
 
         // We're done if we found errors, but we already emitted them.
-        if let Some(reported) = reported {
-            assert!(errors.is_empty());
+        if let Some(reported) = reported
+            && errors.is_empty()
+        {
             return reported;
         }
         assert!(!errors.is_empty());
@@ -1935,7 +1936,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn label_fn_like(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         callable_def_id: Option<DefId>,
         callee_ty: Option<Ty<'tcx>>,
         call_expr: &'tcx hir::Expr<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index c1a32a1afa3..e57717c25d9 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -12,7 +12,7 @@ use core::cmp::min;
 use core::iter;
 use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
 use rustc_data_structures::packed::Pu128;
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind};
@@ -48,7 +48,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .copied()
     }
 
-    pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
+    pub(in super::super) fn suggest_semicolon_at_end(
+        &self,
+        span: Span,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
         // This suggestion is incorrect for
         // fn foo() -> bool { match () { () => true } || match () { () => true } }
         err.span_suggestion_short(
@@ -66,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// - Possible missing return type if the return type is the default, and not `fn main()`.
     pub fn suggest_mismatched_types_on_tail(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -97,7 +101,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     pub(crate) fn suggest_fn_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         found: Ty<'tcx>,
         can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
@@ -179,7 +183,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn suggest_two_fn_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         lhs_expr: &'tcx hir::Expr<'tcx>,
         lhs_ty: Ty<'tcx>,
         rhs_expr: &'tcx hir::Expr<'tcx>,
@@ -253,7 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn suggest_remove_last_method_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
     ) -> bool {
@@ -282,7 +286,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn suggest_deref_ref_or_into(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -540,7 +544,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// in the heap by calling `Box::new()`.
     pub(in super::super) fn suggest_boxing_when_appropriate(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         hir_id: HirId,
         expected: Ty<'tcx>,
@@ -583,7 +587,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// suggest a non-capturing closure
     pub(in super::super) fn suggest_no_capture_closure(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) -> bool {
@@ -620,7 +624,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self, err))]
     pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -732,7 +736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`.
     pub fn suggest_missing_semicolon(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expression: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         needs_block: bool,
@@ -791,7 +795,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "trace", skip(self, err))]
     pub(in super::super) fn suggest_missing_return_type(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         fn_decl: &hir::FnDecl<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -909,7 +913,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     fn try_suggest_return_impl_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         fn_id: hir::HirId,
@@ -1014,7 +1018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn suggest_missing_break_or_return_expr(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &'tcx hir::Expr<'tcx>,
         fn_decl: &hir::FnDecl<'tcx>,
         expected: Ty<'tcx>,
@@ -1118,7 +1122,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn suggest_missing_parentheses(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
     ) -> bool {
         let sp = self.tcx.sess.source_map().start_point(expr.span).with_parent(None);
@@ -1136,7 +1140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// if it was possibly mistaken array syntax.
     pub(crate) fn suggest_block_to_brackets_peeling_refs(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         mut expr: &hir::Expr<'_>,
         mut expr_ty: Ty<'tcx>,
         mut expected_ty: Ty<'tcx>,
@@ -1163,7 +1167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_clone_for_ref(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1198,7 +1202,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_copied_cloned_or_as_ref(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1248,7 +1252,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_into(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1311,7 +1315,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// When expecting a `bool` and finding an `Option`, suggests using `let Some(..)` or `.is_some()`
     pub(crate) fn suggest_option_to_bool(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expr_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1368,7 +1372,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`.
     pub(crate) fn suggest_block_to_brackets(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         blk: &hir::Block<'_>,
         blk_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1408,7 +1412,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self, err))]
     pub(crate) fn suggest_floating_point_literal(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected_ty: Ty<'tcx>,
     ) -> bool {
@@ -1479,7 +1483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self, err))]
     pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected_ty: Ty<'tcx>,
     ) -> bool {
@@ -1517,7 +1521,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_associated_const(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected_ty: Ty<'tcx>,
     ) -> bool {
@@ -1611,7 +1615,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// which is a side-effect of autoref.
     pub(crate) fn note_type_is_not_clone(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         expected_ty: Ty<'tcx>,
         found_ty: Ty<'tcx>,
         expr: &hir::Expr<'_>,
@@ -1811,7 +1815,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         blk: &'tcx hir::Block<'tcx>,
         expected_ty: Ty<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) {
             if let StatementAsExpression::NeedsBoxing = boxed {
@@ -1856,7 +1860,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_missing_unwrap_expect(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -1939,7 +1943,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_coercing_result_via_try_operator(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -1986,7 +1990,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// sole field is of the found type, suggest such variants. (Issue #42764)
     pub(crate) fn suggest_compatible_variants(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
@@ -2175,7 +2179,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_non_zero_new_unwrap(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
@@ -2719,7 +2723,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_cast(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         checked_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -2847,7 +2851,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
 
         let suggest_fallible_into_or_lhs_from =
-            |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
+            |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| {
                 // If we know the expression the expected type is derived from, we might be able
                 // to suggest a widening conversion rather than a narrowing one (which may
                 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
@@ -2887,7 +2891,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
         let suggest_to_change_suffix_or_into =
-            |err: &mut Diagnostic,
+            |err: &mut DiagnosticBuilder<'_>,
              found_to_exp_is_fallible: bool,
              exp_to_found_is_fallible: bool| {
                 let exp_is_lhs = expected_ty_expr.is_some_and(|e| self.tcx.hir().is_lhs(e.hir_id));
@@ -3085,7 +3089,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
     pub(crate) fn suggest_method_call_on_range_literal(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'tcx>,
         checked_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -3163,7 +3167,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// block without a tail expression.
     pub(crate) fn suggest_return_binding_for_missing_tail_expr(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         checked_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/intrinsicck.rs b/compiler/rustc_hir_typeck/src/intrinsicck.rs
index 2dee5093e87..9e3867e630d 100644
--- a/compiler/rustc_hir_typeck/src/intrinsicck.rs
+++ b/compiler/rustc_hir_typeck/src/intrinsicck.rs
@@ -48,8 +48,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let to = normalize(to);
         trace!(?from, ?to);
         if from.has_non_region_infer() || to.has_non_region_infer() {
-            tcx.dcx().span_delayed_bug(span, "argument to transmute has inference variables");
-            return;
+            // Note: this path is currently not reached in any test, so any
+            // example that triggers this would be worth minimizing and
+            // converting into a test.
+            tcx.dcx().span_bug(span, "argument to transmute has inference variables");
         }
         // Transmutes that are only changing lifetimes are always ok.
         if from == to {
diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs
index fefaf996725..c300ec7444b 100644
--- a/compiler/rustc_hir_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs
@@ -570,8 +570,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
             _ => {
                 self.tcx()
                     .dcx()
-                    .span_delayed_bug(span, "struct or tuple struct pattern not applied to an ADT");
-                Err(())
+                    .span_bug(span, "struct or tuple struct pattern not applied to an ADT");
             }
         }
     }
@@ -583,8 +582,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
         match ty.kind() {
             ty::Tuple(args) => Ok(args.len()),
             _ => {
-                self.tcx().dcx().span_delayed_bug(span, "tuple pattern not applied to a tuple");
-                Err(())
+                self.tcx().dcx().span_bug(span, "tuple pattern not applied to a tuple");
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs
index fb969c82d8a..f8d0911a2eb 100644
--- a/compiler/rustc_hir_typeck/src/method/mod.rs
+++ b/compiler/rustc_hir_typeck/src/method/mod.rs
@@ -11,7 +11,7 @@ pub use self::suggest::SelfSource;
 pub use self::MethodError::*;
 
 use crate::FnCtxt;
-use rustc_errors::{Applicability, Diagnostic, SubdiagnosticMessage};
+use rustc_errors::{Applicability, DiagnosticBuilder, SubdiagnosticMessage};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
 use rustc_hir::def_id::DefId;
@@ -126,7 +126,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, err, call_expr))]
     pub(crate) fn suggest_method_call(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         msg: impl Into<SubdiagnosticMessage> + std::fmt::Debug,
         method_name: Ident,
         self_ty: Ty<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index d7edc70bce8..a58e194e20a 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -804,11 +804,10 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         let trait_ref = principal.with_self_ty(self.tcx, self_ty);
         self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| {
             if new_trait_ref.has_non_region_bound_vars() {
-                this.dcx().span_delayed_bug(
+                this.dcx().span_bug(
                     this.span,
                     "tried to select method from HRTB with non-lifetime bound vars",
                 );
-                return;
             }
 
             let new_trait_ref = this.instantiate_bound_regions_with_erased(new_trait_ref);
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 4151298b42f..cc111af5d8f 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -12,8 +12,8 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
-    MultiSpan, StashKey,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, MultiSpan,
+    StashKey,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -34,8 +34,8 @@ use rustc_middle::ty::IsSuggestable;
 use rustc_middle::ty::{self, GenericArgKind, Ty, TyCtxt, TypeVisitableExt};
 use rustc_span::def_id::DefIdSet;
 use rustc_span::symbol::{kw, sym, Ident};
-use rustc_span::Symbol;
 use rustc_span::{edit_distance, ExpnKind, FileName, MacroKind, Span};
+use rustc_span::{Symbol, DUMMY_SP};
 use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
@@ -369,6 +369,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         if let Some(file) = file {
             err.note(format!("the full type name has been written to '{}'", file.display()));
+            err.note(format!(
+                "consider using `--verbose` to print the full type name to the console"
+            ));
         }
 
         err
@@ -493,6 +496,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if let Some(file) = ty_file {
             err.note(format!("the full type name has been written to '{}'", file.display(),));
+            err.note(format!(
+                "consider using `--verbose` to print the full type name to the console"
+            ));
         }
         if rcvr_ty.references_error() {
             err.downgrade_to_delayed_bug();
@@ -1127,7 +1133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        let label_span_not_found = |err: &mut Diagnostic| {
+        let label_span_not_found = |err: &mut DiagnosticBuilder<'_>| {
             if unsatisfied_predicates.is_empty() {
                 err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
                 let is_string_or_ref_str = match rcvr_ty.kind() {
@@ -1438,7 +1444,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self_source: SelfSource<'tcx>,
         args: Option<&'tcx [hir::Expr<'tcx>]>,
         span: Span,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         sources: &mut Vec<CandidateSource>,
         sugg_span: Option<Span>,
     ) {
@@ -1584,7 +1590,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     /// Look at all the associated functions without receivers in the type's inherent impls
     /// to look for builders that return `Self`, `Option<Self>` or `Result<Self, _>`.
-    fn find_builder_fn(&self, err: &mut Diagnostic, rcvr_ty: Ty<'tcx>) {
+    fn find_builder_fn(&self, err: &mut DiagnosticBuilder<'_>, rcvr_ty: Ty<'tcx>) {
         let ty::Adt(adt_def, _) = rcvr_ty.kind() else {
             return;
         };
@@ -1597,7 +1603,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             .filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
             .filter_map(|item| {
                 // Only assoc fns that return `Self`, `Option<Self>` or `Result<Self, _>`.
-                let ret_ty = self.tcx.fn_sig(item.def_id).skip_binder().output();
+                let ret_ty = self
+                    .tcx
+                    .fn_sig(item.def_id)
+                    .instantiate(self.tcx, self.fresh_args_for_item(DUMMY_SP, item.def_id))
+                    .output();
                 let ret_ty = self.tcx.instantiate_bound_regions_with_erased(ret_ty);
                 let ty::Adt(def, args) = ret_ty.kind() else {
                     return None;
@@ -1665,7 +1675,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// doesn't take a `self` receiver.
     fn suggest_associated_call_syntax(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         static_candidates: &Vec<CandidateSource>,
         rcvr_ty: Ty<'tcx>,
         source: SelfSource<'tcx>,
@@ -1809,7 +1819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rcvr_ty: Ty<'tcx>,
         expr: &hir::Expr<'_>,
         item_name: Ident,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         let tcx = self.tcx;
         let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
@@ -2132,7 +2142,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
     fn suggest_calling_method_on_field(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: SelfSource<'tcx>,
         span: Span,
         actual: Ty<'tcx>,
@@ -2212,7 +2222,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_unwrapping_inner_self(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: SelfSource<'tcx>,
         actual: Ty<'tcx>,
         item_name: Ident,
@@ -2401,7 +2411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn note_unmet_impls_on_type(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         errors: Vec<FulfillmentError<'tcx>>,
         suggest_derive: bool,
     ) {
@@ -2484,7 +2494,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn note_predicate_source_and_get_derives(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         unsatisfied_predicates: &[(
             ty::Predicate<'tcx>,
             Option<ty::Predicate<'tcx>>,
@@ -2566,7 +2576,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(crate) fn suggest_derive(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         unsatisfied_predicates: &[(
             ty::Predicate<'tcx>,
             Option<ty::Predicate<'tcx>>,
@@ -2602,7 +2612,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn note_derefed_ty_has_method(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         self_source: SelfSource<'tcx>,
         rcvr_ty: Ty<'tcx>,
         item_name: Ident,
@@ -2682,7 +2692,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_await_before_method(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         item_name: Ident,
         ty: Ty<'tcx>,
         call: &hir::Expr<'_>,
@@ -2705,7 +2715,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) {
+    fn suggest_use_candidates(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        msg: String,
+        candidates: Vec<DefId>,
+    ) {
         let parent_map = self.tcx.visible_parent_map(());
 
         // Separate out candidates that must be imported with a glob, because they are named `_`
@@ -2752,7 +2767,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_valid_traits(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         valid_out_of_scope_traits: Vec<DefId>,
         explain: bool,
     ) -> bool {
@@ -2793,7 +2808,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_traits_to_import(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         rcvr_ty: Ty<'tcx>,
         item_name: Ident,
@@ -3332,7 +3347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// FIXME: currently not working for suggesting `map_or_else`, see #102408
     pub(crate) fn suggest_else_fn_with_closure(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         expr: &hir::Expr<'_>,
         found: Ty<'tcx>,
         expected: Ty<'tcx>,
@@ -3458,7 +3473,7 @@ pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> {
 
 fn print_disambiguation_help<'tcx>(
     tcx: TyCtxt<'tcx>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     source: SelfSource<'tcx>,
     args: Option<&'tcx [hir::Expr<'tcx>]>,
     trait_ref: ty::TraitRef<'tcx>,
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 929b3557f52..fba240bf752 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -5,7 +5,7 @@ use super::FnCtxt;
 use crate::Expectation;
 use rustc_ast as ast;
 use rustc_data_structures::packed::Pu128;
-use rustc_errors::{codes::*, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{codes::*, struct_span_code_err, Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::ObligationCauseCode;
@@ -695,7 +695,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rhs_expr: &'tcx hir::Expr<'tcx>,
         lhs_ty: Ty<'tcx>,
         rhs_ty: Ty<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         is_assign: IsAssign,
         op: hir::BinOp,
     ) -> bool {
diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index 5026fbe4b80..b15c9ef9018 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -3,8 +3,8 @@ use crate::{errors, FnCtxt, LoweredTy};
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+    MultiSpan,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -529,7 +529,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty
     }
 
-    fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) {
+    fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
         if !ty.references_error() {
             err.span_label(span, format!("this is of type `{ty}`"));
         }
@@ -683,7 +683,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_adding_missing_ref_or_removing_ref(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
@@ -715,7 +715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     // Precondition: pat is a Ref(_) pattern
-    fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) {
+    fn borrow_pat_suggestion(&self, err: &mut DiagnosticBuilder<'_>, pat: &Pat<'_>) {
         let tcx = self.tcx;
         if let PatKind::Ref(inner, mutbl) = pat.kind
             && let PatKind::Binding(_, _, binding, ..) = inner.kind
@@ -933,7 +933,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn maybe_suggest_range_literal(
         &self,
-        e: &mut Diagnostic,
+        e: &mut DiagnosticBuilder<'_>,
         opt_def_id: Option<hir::def_id::DefId>,
         ident: Ident,
     ) -> bool {
@@ -1085,10 +1085,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let variant = match res {
             Res::Err => {
-                let e = tcx.dcx().span_delayed_bug(pat.span, "`Res::Err` but no error emitted");
-                self.set_tainted_by_errors(e);
-                on_error(e);
-                return Ty::new_error(tcx, e);
+                tcx.dcx().span_bug(pat.span, "`Res::Err` but no error emitted");
             }
             Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => {
                 let e = report_unexpected_res(res);
diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs
index 23d29916922..dd9c16d006a 100644
--- a/compiler/rustc_incremental/src/persist/fs.rs
+++ b/compiler/rustc_incremental/src/persist/fs.rs
@@ -312,7 +312,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
 
     let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone();
 
-    if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() {
+    if sess.dcx().has_errors_or_delayed_bugs().is_some() {
         // If there have been any errors during compilation, we don't want to
         // publish this session directory. Rather, we'll just delete it.
 
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index ff0c58d09de..32759f5284a 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -32,7 +32,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) {
             return;
         }
         // This is going to be deleted in finalize_session_directory, so let's not create it.
-        if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() {
+        if sess.dcx().has_errors_or_delayed_bugs().is_some() {
             return;
         }
 
@@ -87,7 +87,7 @@ pub fn save_work_product_index(
         return;
     }
     // This is going to be deleted in finalize_session_directory, so let's not create it
-    if sess.dcx().has_errors_or_lint_errors().is_some() {
+    if sess.dcx().has_errors().is_some() {
         return;
     }
 
diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs
index 8bfc05d6a96..f29ba70be98 100644
--- a/compiler/rustc_infer/src/errors/mod.rs
+++ b/compiler/rustc_infer/src/errors/mod.rs
@@ -1,7 +1,8 @@
 use hir::GenericParamKind;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage,
-    DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, DiagnosticMessage,
+    DiagnosticStyledString, EmissionGuarantee, IntoDiagnosticArg, MultiSpan,
+    SubdiagnosticMessageOp,
 };
 use rustc_hir as hir;
 use rustc_hir::FnRetTy;
@@ -225,7 +226,11 @@ pub enum RegionOriginNote<'a> {
 }
 
 impl AddToDiagnostic for RegionOriginNote<'_> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let mut label_or_note = |span, msg: DiagnosticMessage| {
             let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
             let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count();
@@ -286,7 +291,11 @@ pub enum LifetimeMismatchLabels {
 }
 
 impl AddToDiagnostic for LifetimeMismatchLabels {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
                 diag.span_label(param_span, fluent::infer_declared_different);
@@ -330,7 +339,11 @@ pub struct AddLifetimeParamsSuggestion<'a> {
 }
 
 impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let mut mk_suggestion = || {
             let (
                 hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. },
@@ -428,7 +441,11 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq {
 }
 
 impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(mut self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        mut self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         self.unmet_requirements
             .push_span_label(self.binding_span, fluent::infer_msl_introduces_static);
         diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req);
@@ -743,7 +760,11 @@ pub struct ConsiderBorrowingParamHelp {
 }
 
 impl AddToDiagnostic for ConsiderBorrowingParamHelp {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         let mut type_param_span: MultiSpan = self.spans.clone().into();
         for &span in &self.spans {
             // Seems like we can't call f() here as Into<DiagnosticMessage> is required
@@ -784,7 +805,11 @@ pub struct DynTraitConstraintSuggestion {
 }
 
 impl AddToDiagnostic for DynTraitConstraintSuggestion {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         let mut multi_span: MultiSpan = vec![self.span].into();
         multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label);
         multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement);
@@ -827,7 +852,11 @@ pub struct ReqIntroducedLocations {
 }
 
 impl AddToDiagnostic for ReqIntroducedLocations {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(mut self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        mut self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         for sp in self.spans {
             self.span.push_span_label(sp, fluent::infer_ril_introduced_here);
         }
@@ -846,7 +875,11 @@ pub struct MoreTargeted {
 }
 
 impl AddToDiagnostic for MoreTargeted {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.code(E0772);
         diag.primary_message(fluent::infer_more_targeted);
         diag.arg("ident", self.ident);
@@ -1265,7 +1298,11 @@ pub struct SuggestTuplePatternMany {
 }
 
 impl AddToDiagnostic for SuggestTuplePatternMany {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         diag.arg("path", self.path);
         let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into());
         diag.multipart_suggestions(
diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs
index a59a4df7729..c272aa63b08 100644
--- a/compiler/rustc_infer/src/errors/note_and_explain.rs
+++ b/compiler/rustc_infer/src/errors/note_and_explain.rs
@@ -1,6 +1,9 @@
 use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::nice_region_error::find_anon_type;
-use rustc_errors::{AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessageOp};
+use rustc_errors::{
+    AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, IntoDiagnosticArg,
+    SubdiagnosticMessageOp,
+};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{symbol::kw, Span};
 
@@ -160,7 +163,11 @@ impl RegionExplanation<'_> {
 }
 
 impl AddToDiagnostic for RegionExplanation<'_> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        f: F,
+    ) {
         diag.arg("pref_kind", self.prefix);
         diag.arg("suff_kind", self.suffix);
         diag.arg("desc_kind", self.desc.kind);
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 56a45586c9d..9d2e065afa3 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -12,22 +12,19 @@ use crate::infer::canonical::{
     Canonical, CanonicalQueryResponse, CanonicalVarValues, Certainty, OriginalQueryValues,
     QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse,
 };
-use crate::infer::nll_relate::{TypeRelating, TypeRelatingDelegate};
 use crate::infer::region_constraints::{Constraint, RegionConstraintData};
-use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult, NllRegionVariableOrigin};
+use crate::infer::{DefineOpaqueTypes, InferCtxt, InferOk, InferResult};
 use crate::traits::query::NoSolution;
 use crate::traits::{Obligation, ObligationCause, PredicateObligation};
-use crate::traits::{PredicateObligations, TraitEngine, TraitEngineExt};
+use crate::traits::{TraitEngine, TraitEngineExt};
 use rustc_data_structures::captures::Captures;
 use rustc_index::Idx;
 use rustc_index::IndexVec;
 use rustc_middle::arena::ArenaAllocatable;
 use rustc_middle::mir::ConstraintCategory;
 use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::relate::TypeRelation;
-use rustc_middle::ty::{self, BoundVar, ToPredicate, Ty, TyCtxt};
+use rustc_middle::ty::{self, BoundVar, Ty, TyCtxt};
 use rustc_middle::ty::{GenericArg, GenericArgKind};
-use rustc_span::{Span, Symbol};
 use std::fmt::Debug;
 use std::iter;
 
@@ -290,31 +287,19 @@ impl<'tcx> InferCtxt<'tcx> {
                 }
 
                 (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
-                    TypeRelating::new(
-                        self,
-                        QueryTypeRelatingDelegate {
-                            infcx: self,
-                            param_env,
-                            cause,
-                            obligations: &mut obligations,
-                        },
-                        ty::Variance::Invariant,
-                    )
-                    .relate(v1, v2)?;
+                    obligations.extend(
+                        self.at(&cause, param_env)
+                            .eq(DefineOpaqueTypes::Yes, v1, v2)?
+                            .into_obligations(),
+                    );
                 }
 
                 (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
-                    TypeRelating::new(
-                        self,
-                        QueryTypeRelatingDelegate {
-                            infcx: self,
-                            param_env,
-                            cause,
-                            obligations: &mut obligations,
-                        },
-                        ty::Variance::Invariant,
-                    )
-                    .relate(v1, v2)?;
+                    obligations.extend(
+                        self.at(&cause, param_env)
+                            .eq(DefineOpaqueTypes::Yes, v1, v2)?
+                            .into_obligations(),
+                    );
                 }
 
                 _ => {
@@ -405,7 +390,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// will instantiate fresh inference variables for each canonical
     /// variable instead. Therefore, the result of this method must be
     /// properly unified
-    #[instrument(level = "debug", skip(self, cause, param_env))]
+    #[instrument(level = "debug", skip(self, param_env))]
     fn query_response_instantiation_guess<R>(
         &self,
         cause: &ObligationCause<'tcx>,
@@ -697,67 +682,3 @@ pub fn make_query_region_constraints<'tcx>(
 
     QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
 }
-
-struct QueryTypeRelatingDelegate<'a, 'tcx> {
-    infcx: &'a InferCtxt<'tcx>,
-    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: &'a ObligationCause<'tcx>,
-}
-
-impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
-    fn span(&self) -> Span {
-        self.cause.span
-    }
-
-    fn param_env(&self) -> ty::ParamEnv<'tcx> {
-        self.param_env
-    }
-
-    fn create_next_universe(&mut self) -> ty::UniverseIndex {
-        self.infcx.create_next_universe()
-    }
-
-    fn next_existential_region_var(
-        &mut self,
-        from_forall: bool,
-        _name: Option<Symbol>,
-    ) -> ty::Region<'tcx> {
-        let origin = NllRegionVariableOrigin::Existential { from_forall };
-        self.infcx.next_nll_region_var(origin)
-    }
-
-    fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> {
-        ty::Region::new_placeholder(self.infcx.tcx, placeholder)
-    }
-
-    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
-        self.infcx.next_nll_region_var_in_universe(
-            NllRegionVariableOrigin::Existential { from_forall: false },
-            universe,
-        )
-    }
-
-    fn push_outlives(
-        &mut self,
-        sup: ty::Region<'tcx>,
-        sub: ty::Region<'tcx>,
-        _info: ty::VarianceDiagInfo<'tcx>,
-    ) {
-        self.obligations.push(Obligation {
-            cause: self.cause.clone(),
-            param_env: self.param_env,
-            predicate: ty::ClauseKind::RegionOutlives(ty::OutlivesPredicate(sup, sub))
-                .to_predicate(self.infcx.tcx),
-            recursion_depth: 0,
-        });
-    }
-
-    fn forbid_inference_vars() -> bool {
-        true
-    }
-
-    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
-        self.obligations.extend(obligations);
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 9bfaeb5ee32..505d56cf491 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -60,8 +60,8 @@ use crate::traits::{
 
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, DiagCtxt, Diagnostic,
-    DiagnosticBuilder, DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagCtxt, DiagnosticBuilder,
+    DiagnosticStyledString, ErrorGuaranteed, IntoDiagnosticArg,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -155,7 +155,7 @@ impl<'tcx> Deref for TypeErrCtxt<'_, 'tcx> {
 
 pub(super) fn note_and_explain_region<'tcx>(
     tcx: TyCtxt<'tcx>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     prefix: &str,
     region: ty::Region<'tcx>,
     suffix: &str,
@@ -180,7 +180,7 @@ pub(super) fn note_and_explain_region<'tcx>(
 
 fn explain_free_region<'tcx>(
     tcx: TyCtxt<'tcx>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     prefix: &str,
     region: ty::Region<'tcx>,
     suffix: &str,
@@ -262,7 +262,7 @@ fn msg_span_from_named_region<'tcx>(
 }
 
 fn emit_msg_span(
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     prefix: &str,
     description: String,
     span: Option<Span>,
@@ -278,7 +278,7 @@ fn emit_msg_span(
 }
 
 fn label_msg_span(
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     prefix: &str,
     description: String,
     span: Option<Span>,
@@ -577,7 +577,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     }
 
     /// Adds a note if the types come from similarly named crates
-    fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: TypeError<'tcx>) {
+    fn check_and_note_conflicting_crates(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        terr: TypeError<'tcx>,
+    ) {
         use hir::def_id::CrateNum;
         use rustc_hir::definitions::DisambiguatedDefPathData;
         use ty::print::Printer;
@@ -651,7 +655,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
             }
         }
 
-        let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
+        let report_path_match = |err: &mut DiagnosticBuilder<'_>, did1: DefId, did2: DefId| {
             // Only report definitions from different crates. If both definitions
             // are from a local module we could have false positives, e.g.
             // let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
@@ -701,7 +705,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn note_error_origin(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         cause: &ObligationCause<'tcx>,
         exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
         terr: TypeError<'tcx>,
@@ -1535,7 +1539,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     )]
     pub fn note_type_err(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         cause: &ObligationCause<'tcx>,
         secondary_span: Option<(Span, Cow<'static, str>)>,
         mut values: Option<ValuePairs<'tcx>>,
@@ -1582,14 +1586,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 types_visitor
             }
 
-            fn report(&self, err: &mut Diagnostic) {
+            fn report(&self, err: &mut DiagnosticBuilder<'_>) {
                 self.add_labels_for_types(err, "expected", &self.expected);
                 self.add_labels_for_types(err, "found", &self.found);
             }
 
             fn add_labels_for_types(
                 &self,
-                err: &mut Diagnostic,
+                err: &mut DiagnosticBuilder<'_>,
                 target: &str,
                 types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,
             ) {
@@ -1803,7 +1807,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         |prim: Ty<'tcx>,
                          shadow: Ty<'tcx>,
                          defid: DefId,
-                         diagnostic: &mut Diagnostic| {
+                         diagnostic: &mut DiagnosticBuilder<'_>| {
                             let name = shadow.sort_string(self.tcx);
                             diagnostic.note(format!(
                             "{prim} and {name} have similar names, but are actually distinct types"
@@ -1823,7 +1827,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     let diagnose_adts =
                         |expected_adt: ty::AdtDef<'tcx>,
                          found_adt: ty::AdtDef<'tcx>,
-                         diagnostic: &mut Diagnostic| {
+                         diagnostic: &mut DiagnosticBuilder<'_>| {
                             let found_name = values.found.sort_string(self.tcx);
                             let expected_name = values.expected.sort_string(self.tcx);
 
@@ -1931,6 +1935,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                                         "the full type name has been written to '{}'",
                                         path.display(),
                                     ));
+                                    diag.note(format!("consider using `--verbose` to print the full type name to the console"));
                                 }
                             }
                         }
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
index adb3267d5be..cf8ac544106 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -12,7 +12,7 @@ use crate::infer::SubregionOrigin;
 use crate::infer::TyCtxt;
 
 use rustc_errors::AddToDiagnostic;
-use rustc_errors::{Diagnostic, ErrorGuaranteed};
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir::Ty;
 use rustc_middle::ty::Region;
 
@@ -142,7 +142,7 @@ pub fn suggest_adding_lifetime_params<'tcx>(
     sub: Region<'tcx>,
     ty_sup: &'tcx Ty<'_>,
     ty_sub: &'tcx Ty<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
 ) {
     let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false };
     suggestion.add_to_diagnostic(err);
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index c8fd4e3a692..83e0b763d24 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -9,7 +9,7 @@ use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{AddToDiagnostic, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
@@ -261,7 +261,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
 pub fn suggest_new_region_bound(
     tcx: TyCtxt<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     fn_returns: Vec<&rustc_hir::Ty<'_>>,
     lifetime_name: String,
     arg: Option<String>,
@@ -488,7 +488,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// `'static` obligation. Suggest relaxing that implicit bound.
     fn find_impl_on_dyn_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ty: Ty<'_>,
         ctxt: &UnifyReceiverContext<'tcx>,
     ) -> bool {
@@ -521,7 +521,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
     fn suggest_constrain_dyn_trait_in_impl(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         found_dids: &FxIndexSet<DefId>,
         ident: Ident,
         self_ty: &hir::Ty<'_>,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index bfff00b948e..f1f8314661f 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -5,7 +5,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::TyCtxt;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
-use rustc_middle::ty::{self, Binder, Region, Ty, TypeVisitable};
+use rustc_middle::ty::{self, Binder, Region, Ty, TypeFoldable};
 use rustc_span::Span;
 
 /// Information about the anonymous region we are searching for.
@@ -142,10 +142,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
     fn includes_region(
         &self,
-        ty: Binder<'tcx, impl TypeVisitable<TyCtxt<'tcx>>>,
+        ty: Binder<'tcx, impl TypeFoldable<TyCtxt<'tcx>>>,
         region: ty::BoundRegionKind,
     ) -> bool {
-        let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty);
+        let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(ty);
         // We are only checking is any region meets the condition so order doesn't matter
         #[allow(rustc::potential_query_instability)]
         late_bound_regions.iter().any(|r| *r == region)
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 50ac6235deb..0878505e85e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -5,7 +5,7 @@ use crate::errors::{
 use crate::fluent_generated as fluent;
 use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
 use crate::infer::{self, SubregionOrigin};
-use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder};
+use rustc_errors::{AddToDiagnostic, DiagnosticBuilder};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
@@ -15,7 +15,11 @@ use rustc_span::symbol::kw;
 use super::ObligationCauseAsDiagArg;
 
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
-    pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
+    pub(super) fn note_region_origin(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        origin: &SubregionOrigin<'tcx>,
+    ) {
         match *origin {
             infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
                 span: trace.cause.span,
@@ -290,7 +294,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         trait_item_def_id: DefId,
         impl_item_def_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         // FIXME(compiler-errors): Right now this is only being used for region
         // predicate mismatches. Ideally, we'd use it for *all* predicate mismatches,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
index 3c42f13141d..9df2f929501 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_and_explain.rs
@@ -1,6 +1,6 @@
 use super::TypeErrCtxt;
 use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
-use rustc_errors::{pluralize, Diagnostic, MultiSpan};
+use rustc_errors::{pluralize, DiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_middle::traits::ObligationCauseCode;
@@ -15,7 +15,7 @@ use rustc_span::{def_id::DefId, sym, BytePos, Span, Symbol};
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     pub fn note_and_explain_type_err(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         err: TypeError<'tcx>,
         cause: &ObligationCause<'tcx>,
         sp: Span,
@@ -522,7 +522,7 @@ impl<T> Trait<T> for X {
 
     fn suggest_constraint(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         msg: impl Fn() -> String,
         body_owner_def_id: DefId,
         proj_ty: &ty::AliasTy<'tcx>,
@@ -595,7 +595,7 @@ impl<T> Trait<T> for X {
     ///    fn that returns the type.
     fn expected_projection(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         proj_ty: &ty::AliasTy<'tcx>,
         values: ExpectedFound<Ty<'tcx>>,
         body_owner_def_id: DefId,
@@ -705,7 +705,7 @@ fn foo(&self) -> Self::T { String::new() }
     /// a return type. This can occur when dealing with `TryStream` (#71035).
     fn suggest_constraining_opaque_associated_type(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         msg: impl Fn() -> String,
         proj_ty: &ty::AliasTy<'tcx>,
         ty: Ty<'tcx>,
@@ -740,7 +740,7 @@ fn foo(&self) -> Self::T { String::new() }
 
     fn point_at_methods_that_satisfy_associated_type(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         assoc_container_id: DefId,
         current_method_ident: Option<Symbol>,
         proj_ty_item_def_id: DefId,
@@ -798,7 +798,7 @@ fn foo(&self) -> Self::T { String::new() }
 
     fn point_at_associated_type(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         body_owner_def_id: DefId,
         found: Ty<'tcx>,
     ) -> bool {
@@ -879,7 +879,7 @@ fn foo(&self) -> Self::T { String::new() }
     /// type is defined on a supertrait of the one present in the bounds.
     fn constrain_generic_bound_associated_type_structured_suggestion(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::TraitRef<'tcx>,
         bounds: hir::GenericBounds<'_>,
         assoc: ty::AssocItem,
@@ -916,7 +916,7 @@ fn foo(&self) -> Self::T { String::new() }
     /// associated type to a given type `ty`.
     fn constrain_associated_type_structured_suggestion(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         span: Span,
         assoc: ty::AssocItem,
         assoc_args: &[ty::GenericArg<'tcx>],
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
index 15834c78413..f7102ab6205 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -1,7 +1,7 @@
 use hir::def::CtorKind;
 use hir::intravisit::{walk_expr, walk_stmt, Visitor};
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_middle::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
@@ -76,7 +76,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     pub(super) fn suggest_boxing_for_return_impl_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         return_sp: Span,
         arm_spans: impl Iterator<Item = Span>,
     ) {
@@ -100,7 +100,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         cause: &ObligationCause<'tcx>,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
         // some modifications due to that being in typeck and this being in infer.
@@ -177,7 +177,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         cause: &ObligationCause<'tcx>,
         exp_span: Span,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         debug!(
             "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
@@ -258,7 +258,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         cause: &ObligationCause<'tcx>,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         debug!(
             "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
@@ -298,7 +298,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         cause: &ObligationCause<'tcx>,
         span: Span,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
         let ty::error::ExpectedFound { expected, found } = exp_found;
@@ -532,7 +532,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         span: Span,
         hir: hir::Node<'_>,
         exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
     ) {
         // 0. Extract fn_decl from hir
         let hir::Node::Expr(hir::Expr {
@@ -818,7 +818,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         blk: &'tcx hir::Block<'tcx>,
         expected_ty: Ty<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         let diag = self.consider_returning_binding_diag(blk, expected_ty);
         match diag {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 243558b11a8..8fc71671b27 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -54,7 +54,6 @@ use self::region_constraints::{
     RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot,
 };
 pub use self::relate::combine::CombineFields;
-pub use self::relate::nll as nll_relate;
 use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 
 pub mod at;
@@ -713,7 +712,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
             reported_trait_errors: Default::default(),
             reported_signature_mismatch: Default::default(),
             tainted_by_errors: Cell::new(None),
-            err_count_on_creation: tcx.dcx().err_count(),
+            err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(),
             stashed_err_count_on_creation: tcx.dcx().stashed_err_count(),
             universe: Cell::new(ty::UniverseIndex::ROOT),
             intercrate,
@@ -1268,8 +1267,11 @@ impl<'tcx> InferCtxt<'tcx> {
     pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> {
         if let Some(guar) = self.tainted_by_errors.get() {
             Some(guar)
-        } else if self.dcx().err_count() > self.err_count_on_creation {
-            // Errors reported since this infcx was made.
+        } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation {
+            // Errors reported since this infcx was made. Lint errors are
+            // excluded to avoid some being swallowed in the presence of
+            // non-lint errors. (It's arguable whether or not this exclusion is
+            // important.)
             let guar = self.dcx().has_errors().unwrap();
             self.set_tainted_by_errors(guar);
             Some(guar)
diff --git a/compiler/rustc_infer/src/infer/relate/combine.rs b/compiler/rustc_infer/src/infer/relate/combine.rs
index 7edfbf02a68..f7690831c2a 100644
--- a/compiler/rustc_infer/src/infer/relate/combine.rs
+++ b/compiler/rustc_infer/src/infer/relate/combine.rs
@@ -23,19 +23,18 @@
 //! this should be correctly updated.
 
 use super::equate::Equate;
-use super::generalize::{self, CombineDelegate, Generalization};
 use super::glb::Glb;
 use super::lub::Lub;
 use super::sub::Sub;
 use crate::infer::{DefineOpaqueTypes, InferCtxt, TypeTrace};
 use crate::traits::{Obligation, PredicateObligations};
 use rustc_middle::infer::canonical::OriginalQueryValues;
-use rustc_middle::infer::unify_key::{ConstVariableValue, EffectVarValue};
+use rustc_middle::infer::unify_key::EffectVarValue;
 use rustc_middle::ty::error::{ExpectedFound, TypeError};
 use rustc_middle::ty::relate::{RelateResult, TypeRelation};
 use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
-use rustc_middle::ty::{AliasRelationDirection, TyVar};
 use rustc_middle::ty::{IntType, UintType};
+use rustc_span::Span;
 
 #[derive(Clone)]
 pub struct CombineFields<'infcx, 'tcx> {
@@ -195,7 +194,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 ty::ConstKind::Infer(InferConst::Var(b_vid)),
             ) => {
                 self.inner.borrow_mut().const_unification_table().union(a_vid, b_vid);
-                return Ok(a);
+                Ok(a)
             }
 
             (
@@ -203,7 +202,7 @@ impl<'tcx> InferCtxt<'tcx> {
                 ty::ConstKind::Infer(InferConst::EffectVar(b_vid)),
             ) => {
                 self.inner.borrow_mut().effect_unification_table().union(a_vid, b_vid);
-                return Ok(a);
+                Ok(a)
             }
 
             // All other cases of inference with other variables are errors.
@@ -221,19 +220,21 @@ impl<'tcx> InferCtxt<'tcx> {
             }
 
             (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
-                return self.unify_const_variable(vid, b);
+                self.instantiate_const_var(relation, relation.a_is_expected(), vid, b)?;
+                Ok(b)
             }
 
             (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
-                return self.unify_const_variable(vid, a);
+                self.instantiate_const_var(relation, !relation.a_is_expected(), vid, a)?;
+                Ok(a)
             }
 
             (ty::ConstKind::Infer(InferConst::EffectVar(vid)), _) => {
-                return Ok(self.unify_effect_variable(vid, b));
+                Ok(self.unify_effect_variable(vid, b))
             }
 
             (_, ty::ConstKind::Infer(InferConst::EffectVar(vid))) => {
-                return Ok(self.unify_effect_variable(vid, a));
+                Ok(self.unify_effect_variable(vid, a))
             }
 
             (ty::ConstKind::Unevaluated(..), _) | (_, ty::ConstKind::Unevaluated(..))
@@ -241,7 +242,7 @@ impl<'tcx> InferCtxt<'tcx> {
             {
                 let (a, b) = if relation.a_is_expected() { (a, b) } else { (b, a) };
 
-                relation.register_predicates([ty::Binder::dummy(if self.next_trait_solver() {
+                relation.register_predicates([if self.next_trait_solver() {
                     ty::PredicateKind::AliasRelate(
                         a.into(),
                         b.into(),
@@ -249,77 +250,12 @@ impl<'tcx> InferCtxt<'tcx> {
                     )
                 } else {
                     ty::PredicateKind::ConstEquate(a, b)
-                })]);
+                }]);
 
-                return Ok(b);
+                Ok(b)
             }
-            _ => {}
+            _ => ty::relate::structurally_relate_consts(relation, a, b),
         }
-
-        ty::relate::structurally_relate_consts(relation, a, b)
-    }
-
-    /// Unifies the const variable `target_vid` with the given constant.
-    ///
-    /// This also tests if the given const `ct` contains an inference variable which was previously
-    /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
-    /// would result in an infinite type as we continuously replace an inference variable
-    /// in `ct` with `ct` itself.
-    ///
-    /// This is especially important as unevaluated consts use their parents generics.
-    /// They therefore often contain unused args, making these errors far more likely.
-    ///
-    /// A good example of this is the following:
-    ///
-    /// ```compile_fail,E0308
-    /// #![feature(generic_const_exprs)]
-    ///
-    /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
-    ///     todo!()
-    /// }
-    ///
-    /// fn main() {
-    ///     let mut arr = Default::default();
-    ///     arr = bind(arr);
-    /// }
-    /// ```
-    ///
-    /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
-    /// of `fn bind` (meaning that its args contain `N`).
-    ///
-    /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
-    /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
-    ///
-    /// As `3 + 4` contains `N` in its args, this must not succeed.
-    ///
-    /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
-    #[instrument(level = "debug", skip(self))]
-    fn unify_const_variable(
-        &self,
-        target_vid: ty::ConstVid,
-        ct: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        let span = match self.inner.borrow_mut().const_unification_table().probe_value(target_vid) {
-            ConstVariableValue::Known { value } => {
-                bug!("instantiating a known const var: {target_vid:?} {value} {ct}")
-            }
-            ConstVariableValue::Unknown { origin, universe: _ } => origin.span,
-        };
-        // FIXME(generic_const_exprs): Occurs check failures for unevaluated
-        // constants and generic expressions are not yet handled correctly.
-        let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize(
-            self,
-            &mut CombineDelegate { infcx: self, span },
-            ct,
-            target_vid,
-            ty::Variance::Invariant,
-        )?;
-
-        self.inner
-            .borrow_mut()
-            .const_unification_table()
-            .union_value(target_vid, ConstVariableValue::Known { value });
-        Ok(value)
     }
 
     fn unify_integral_variable(
@@ -383,131 +319,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         Glb::new(self, a_is_expected)
     }
 
-    /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`.
-    /// The idea is that we should ensure that the type `a_ty` is equal
-    /// to, a subtype of, or a supertype of (respectively) the type
-    /// to which `b_vid` is bound.
-    ///
-    /// Since `b_vid` has not yet been instantiated with a type, we
-    /// will first instantiate `b_vid` with a *generalized* version
-    /// of `a_ty`. Generalization introduces other inference
-    /// variables wherever subtyping could occur.
-    #[instrument(skip(self), level = "debug")]
-    pub fn instantiate(
-        &mut self,
-        a_ty: Ty<'tcx>,
-        ambient_variance: ty::Variance,
-        b_vid: ty::TyVid,
-        a_is_expected: bool,
-    ) -> RelateResult<'tcx, ()> {
-        // Get the actual variable that b_vid has been inferred to
-        debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
-
-        // Generalize type of `a_ty` appropriately depending on the
-        // direction. As an example, assume:
-        //
-        // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an
-        //   inference variable,
-        // - and `dir` == `SubtypeOf`.
-        //
-        // Then the generalized form `b_ty` would be `&'?2 ?3`, where
-        // `'?2` and `?3` are fresh region/type inference
-        // variables. (Down below, we will relate `a_ty <: b_ty`,
-        // adding constraints like `'x: '?2` and `?1 <: ?3`.)
-        let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize(
-            self.infcx,
-            &mut CombineDelegate { infcx: self.infcx, span: self.trace.span() },
-            a_ty,
-            b_vid,
-            ambient_variance,
-        )?;
-
-        // Constrain `b_vid` to the generalized type `b_ty`.
-        if let &ty::Infer(TyVar(b_ty_vid)) = b_ty.kind() {
-            self.infcx.inner.borrow_mut().type_variables().equate(b_vid, b_ty_vid);
-        } else {
-            self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
-        }
-
-        if needs_wf {
-            self.obligations.push(Obligation::new(
-                self.tcx(),
-                self.trace.cause.clone(),
-                self.param_env,
-                ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
-                    b_ty.into(),
-                ))),
-            ));
-        }
-
-        // Finally, relate `b_ty` to `a_ty`, as described in previous comment.
-        //
-        // FIXME(#16847): This code is non-ideal because all these subtype
-        // relations wind up attributed to the same spans. We need
-        // to associate causes/spans with each of the relations in
-        // the stack to get this right.
-        if b_ty.is_ty_var() {
-            // This happens for cases like `<?0 as Trait>::Assoc == ?0`.
-            // We can't instantiate `?0` here as that would result in a
-            // cyclic type. We instead delay the unification in case
-            // the alias can be normalized to something which does not
-            // mention `?0`.
-            if self.infcx.next_trait_solver() {
-                let (lhs, rhs, direction) = match ambient_variance {
-                    ty::Variance::Invariant => {
-                        (a_ty.into(), b_ty.into(), AliasRelationDirection::Equate)
-                    }
-                    ty::Variance::Covariant => {
-                        (a_ty.into(), b_ty.into(), AliasRelationDirection::Subtype)
-                    }
-                    ty::Variance::Contravariant => {
-                        (b_ty.into(), a_ty.into(), AliasRelationDirection::Subtype)
-                    }
-                    ty::Variance::Bivariant => unreachable!("bivariant generalization"),
-                };
-                self.obligations.push(Obligation::new(
-                    self.tcx(),
-                    self.trace.cause.clone(),
-                    self.param_env,
-                    ty::PredicateKind::AliasRelate(lhs, rhs, direction),
-                ));
-            } else {
-                match a_ty.kind() {
-                    &ty::Alias(ty::Projection, data) => {
-                        // FIXME: This does not handle subtyping correctly, we could
-                        // instead create a new inference variable for `a_ty`, emitting
-                        // `Projection(a_ty, a_infer)` and `a_infer <: b_ty`.
-                        self.obligations.push(Obligation::new(
-                            self.tcx(),
-                            self.trace.cause.clone(),
-                            self.param_env,
-                            ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
-                        ))
-                    }
-                    // The old solver only accepts projection predicates for associated types.
-                    ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => {
-                        return Err(TypeError::CyclicTy(a_ty));
-                    }
-                    _ => bug!("generalizated `{a_ty:?} to infer, not an alias"),
-                }
-            }
-        } else {
-            match ambient_variance {
-                ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
-                ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
-                ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
-                    ty::Contravariant,
-                    ty::VarianceDiagInfo::default(),
-                    a_ty,
-                    b_ty,
-                ),
-                ty::Variance::Bivariant => unreachable!("bivariant generalization"),
-            }?;
-        }
-
-        Ok(())
-    }
-
     pub fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
         self.obligations.extend(obligations);
     }
@@ -520,6 +331,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
 }
 
 pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
+    fn span(&self) -> Span;
+
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
 
     /// Register obligations that must hold in order for this relation to hold
diff --git a/compiler/rustc_infer/src/infer/relate/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs
index cb62f258373..aefa9a5a0d6 100644
--- a/compiler/rustc_infer/src/infer/relate/equate.rs
+++ b/compiler/rustc_infer/src/infer/relate/equate.rs
@@ -8,6 +8,7 @@ use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 
 use rustc_hir::def_id::DefId;
+use rustc_span::Span;
 
 /// Ensures `a` is made equal to `b`. Returns `a` on success.
 pub struct Equate<'combine, 'infcx, 'tcx> {
@@ -81,12 +82,17 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
                 infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
             }
 
-            (&ty::Infer(TyVar(a_id)), _) => {
-                self.fields.instantiate(b, ty::Invariant, a_id, self.a_is_expected)?;
+            (&ty::Infer(TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Invariant, b)?;
             }
 
-            (_, &ty::Infer(TyVar(b_id))) => {
-                self.fields.instantiate(a, ty::Invariant, b_id, self.a_is_expected)?;
+            (_, &ty::Infer(TyVar(b_vid))) => {
+                infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Invariant, a)?;
+            }
+
+            (&ty::Error(e), _) | (_, &ty::Error(e)) => {
+                infcx.set_tainted_by_errors(e);
+                return Ok(Ty::new_error(self.tcx(), e));
             }
 
             (
@@ -170,6 +176,10 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
 }
 
 impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> {
+    fn span(&self) -> Span {
+        self.fields.trace.span()
+    }
+
     fn param_env(&self) -> ty::ParamEnv<'tcx> {
         self.fields.param_env
     }
diff --git a/compiler/rustc_infer/src/infer/relate/generalize.rs b/compiler/rustc_infer/src/infer/relate/generalize.rs
index bc16d613ccb..90f10a0eba9 100644
--- a/compiler/rustc_infer/src/infer/relate/generalize.rs
+++ b/compiler/rustc_infer/src/infer/relate/generalize.rs
@@ -1,5 +1,7 @@
 use std::mem;
 
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
+use crate::infer::{InferCtxt, ObligationEmittingRelation, RegionVariableOrigin};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir::def_id::DefId;
@@ -7,98 +9,241 @@ use rustc_middle::infer::unify_key::ConstVariableValue;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::visit::MaxUniverse;
-use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{AliasRelationDirection, InferConst, Term, TypeVisitable, TypeVisitableExt};
 use rustc_span::Span;
 
-use crate::infer::nll_relate::TypeRelatingDelegate;
-use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
-use crate::infer::{InferCtxt, RegionVariableOrigin};
-
-/// Attempts to generalize `term` for the type variable `for_vid`.
-/// This checks for cycles -- that is, whether the type `term`
-/// references `for_vid`.
-pub fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>> + Relate<'tcx>>(
-    infcx: &InferCtxt<'tcx>,
-    delegate: &mut D,
-    term: T,
-    for_vid: impl Into<ty::TermVid>,
-    ambient_variance: ty::Variance,
-) -> RelateResult<'tcx, Generalization<T>> {
-    let (for_universe, root_vid) = match for_vid.into() {
-        ty::TermVid::Ty(ty_vid) => (
-            infcx.probe_ty_var(ty_vid).unwrap_err(),
-            ty::TermVid::Ty(infcx.inner.borrow_mut().type_variables().sub_root_var(ty_vid)),
-        ),
-        ty::TermVid::Const(ct_vid) => (
-            infcx.probe_const_var(ct_vid).unwrap_err(),
-            ty::TermVid::Const(infcx.inner.borrow_mut().const_unification_table().find(ct_vid).vid),
-        ),
-    };
-
-    let mut generalizer = Generalizer {
-        infcx,
-        delegate,
-        ambient_variance,
-        root_vid,
-        for_universe,
-        root_term: term.into(),
-        in_alias: false,
-        needs_wf: false,
-        cache: Default::default(),
-    };
-
-    assert!(!term.has_escaping_bound_vars());
-    let value_may_be_infer = generalizer.relate(term, term)?;
-    let needs_wf = generalizer.needs_wf;
-    Ok(Generalization { value_may_be_infer, needs_wf })
-}
-
-/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
-/// in the generalizer code.
-pub trait GeneralizerDelegate<'tcx> {
-    fn forbid_inference_vars() -> bool;
-
-    fn span(&self) -> Span;
+impl<'tcx> InferCtxt<'tcx> {
+    /// The idea is that we should ensure that the type variable `target_vid`
+    /// is equal to, a subtype of, or a supertype of `source_ty`.
+    ///
+    /// For this, we will instantiate `target_vid` with a *generalized* version
+    /// of `source_ty`. Generalization introduces other inference variables wherever
+    /// subtyping could occur. This also does the occurs checks, detecting whether
+    /// instantiating `target_vid` would result in a cyclic type. We eagerly error
+    /// in this case.
+    ///
+    /// This is *not* expected to be used anywhere except for an implementation of
+    /// `TypeRelation`. Do not use this, and instead please use `At::eq`, for all
+    /// other usecases (i.e. setting the value of a type var).
+    #[instrument(level = "debug", skip(self, relation, target_is_expected))]
+    pub fn instantiate_ty_var<R: ObligationEmittingRelation<'tcx>>(
+        &self,
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::TyVid,
+        ambient_variance: ty::Variance,
+        source_ty: Ty<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        debug_assert!(self.inner.borrow_mut().type_variables().probe(target_vid).is_unknown());
+
+        // Generalize `source_ty` depending on the current variance. As an example, assume
+        // `?target <: &'x ?1`, where `'x` is some free region and `?1` is an inference
+        // variable.
+        //
+        // Then the `generalized_ty` would be `&'?2 ?3`, where `'?2` and `?3` are fresh
+        // region/type inference variables.
+        //
+        // We then relate `generalized_ty <: source_ty`,adding constraints like `'x: '?2` and `?1 <: ?3`.
+        let Generalization { value_may_be_infer: generalized_ty, has_unconstrained_ty_var } =
+            self.generalize(relation.span(), target_vid, ambient_variance, source_ty)?;
+
+        // Constrain `b_vid` to the generalized type `generalized_ty`.
+        if let &ty::Infer(ty::TyVar(generalized_vid)) = generalized_ty.kind() {
+            self.inner.borrow_mut().type_variables().equate(target_vid, generalized_vid);
+        } else {
+            self.inner.borrow_mut().type_variables().instantiate(target_vid, generalized_ty);
+        }
 
-    fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
-}
+        // See the comment on `Generalization::has_unconstrained_ty_var`.
+        if has_unconstrained_ty_var {
+            relation.register_predicates([ty::ClauseKind::WellFormed(generalized_ty.into())]);
+        }
 
-pub struct CombineDelegate<'cx, 'tcx> {
-    pub infcx: &'cx InferCtxt<'tcx>,
-    pub span: Span,
-}
+        // Finally, relate `generalized_ty` to `source_ty`, as described in previous comment.
+        //
+        // FIXME(#16847): This code is non-ideal because all these subtype
+        // relations wind up attributed to the same spans. We need
+        // to associate causes/spans with each of the relations in
+        // the stack to get this right.
+        if generalized_ty.is_ty_var() {
+            // This happens for cases like `<?0 as Trait>::Assoc == ?0`.
+            // We can't instantiate `?0` here as that would result in a
+            // cyclic type. We instead delay the unification in case
+            // the alias can be normalized to something which does not
+            // mention `?0`.
+            if self.next_trait_solver() {
+                let (lhs, rhs, direction) = match ambient_variance {
+                    ty::Variance::Invariant => {
+                        (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Equate)
+                    }
+                    ty::Variance::Covariant => {
+                        (generalized_ty.into(), source_ty.into(), AliasRelationDirection::Subtype)
+                    }
+                    ty::Variance::Contravariant => {
+                        (source_ty.into(), generalized_ty.into(), AliasRelationDirection::Subtype)
+                    }
+                    ty::Variance::Bivariant => unreachable!("bivariant generalization"),
+                };
 
-impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
-    fn forbid_inference_vars() -> bool {
-        false
-    }
+                relation.register_predicates([ty::PredicateKind::AliasRelate(lhs, rhs, direction)]);
+            } else {
+                match source_ty.kind() {
+                    &ty::Alias(ty::Projection, data) => {
+                        // FIXME: This does not handle subtyping correctly, we could
+                        // instead create a new inference variable `?normalized_source`, emitting
+                        // `Projection(normalized_source, ?ty_normalized)` and `?normalized_source <: generalized_ty`.
+                        relation.register_predicates([ty::ProjectionPredicate {
+                            projection_ty: data,
+                            term: generalized_ty.into(),
+                        }]);
+                    }
+                    // The old solver only accepts projection predicates for associated types.
+                    ty::Alias(ty::Inherent | ty::Weak | ty::Opaque, _) => {
+                        return Err(TypeError::CyclicTy(source_ty));
+                    }
+                    _ => bug!("generalized `{source_ty:?} to infer, not an alias"),
+                }
+            }
+        } else {
+            // HACK: make sure that we `a_is_expected` continues to be
+            // correct when relating the generalized type with the source.
+            if target_is_expected == relation.a_is_expected() {
+                relation.relate_with_variance(
+                    ambient_variance,
+                    ty::VarianceDiagInfo::default(),
+                    generalized_ty,
+                    source_ty,
+                )?;
+            } else {
+                relation.relate_with_variance(
+                    ambient_variance.xform(ty::Contravariant),
+                    ty::VarianceDiagInfo::default(),
+                    source_ty,
+                    generalized_ty,
+                )?;
+            }
+        }
 
-    fn span(&self) -> Span {
-        self.span
+        Ok(())
     }
 
-    fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
-        // FIXME: This is non-ideal because we don't give a
-        // very descriptive origin for this region variable.
-        self.infcx
-            .next_region_var_in_universe(RegionVariableOrigin::MiscVariable(self.span), universe)
-    }
-}
+    /// Instantiates the const variable `target_vid` with the given constant.
+    ///
+    /// This also tests if the given const `ct` contains an inference variable which was previously
+    /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+    /// would result in an infinite type as we continuously replace an inference variable
+    /// in `ct` with `ct` itself.
+    ///
+    /// This is especially important as unevaluated consts use their parents generics.
+    /// They therefore often contain unused args, making these errors far more likely.
+    ///
+    /// A good example of this is the following:
+    ///
+    /// ```compile_fail,E0308
+    /// #![feature(generic_const_exprs)]
+    ///
+    /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+    ///     todo!()
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut arr = Default::default();
+    ///     arr = bind(arr);
+    /// }
+    /// ```
+    ///
+    /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+    /// of `fn bind` (meaning that its args contain `N`).
+    ///
+    /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+    /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+    ///
+    /// As `3 + 4` contains `N` in its args, this must not succeed.
+    ///
+    /// See `tests/ui/const-generics/occurs-check/` for more examples where this is relevant.
+    #[instrument(level = "debug", skip(self, relation))]
+    pub(super) fn instantiate_const_var<R: ObligationEmittingRelation<'tcx>>(
+        &self,
+        relation: &mut R,
+        target_is_expected: bool,
+        target_vid: ty::ConstVid,
+        source_ct: ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, ()> {
+        // FIXME(generic_const_exprs): Occurs check failures for unevaluated
+        // constants and generic expressions are not yet handled correctly.
+        let Generalization { value_may_be_infer: generalized_ct, has_unconstrained_ty_var } =
+            self.generalize(relation.span(), target_vid, ty::Variance::Invariant, source_ct)?;
+
+        debug_assert!(!generalized_ct.is_ct_infer());
+        if has_unconstrained_ty_var {
+            bug!("unconstrained ty var when generalizing `{source_ct:?}`");
+        }
 
-impl<'tcx, T> GeneralizerDelegate<'tcx> for T
-where
-    T: TypeRelatingDelegate<'tcx>,
-{
-    fn forbid_inference_vars() -> bool {
-        <Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
-    }
+        self.inner
+            .borrow_mut()
+            .const_unification_table()
+            .union_value(target_vid, ConstVariableValue::Known { value: generalized_ct });
+
+        // HACK: make sure that we `a_is_expected` continues to be
+        // correct when relating the generalized type with the source.
+        if target_is_expected == relation.a_is_expected() {
+            relation.relate_with_variance(
+                ty::Variance::Invariant,
+                ty::VarianceDiagInfo::default(),
+                generalized_ct,
+                source_ct,
+            )?;
+        } else {
+            relation.relate_with_variance(
+                ty::Variance::Invariant,
+                ty::VarianceDiagInfo::default(),
+                source_ct,
+                generalized_ct,
+            )?;
+        }
 
-    fn span(&self) -> Span {
-        <Self as TypeRelatingDelegate<'tcx>>::span(&self)
+        Ok(())
     }
 
-    fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
-        <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
+    /// Attempts to generalize `source_term` for the type variable `target_vid`.
+    /// This checks for cycles -- that is, whether `source_term` references `target_vid`.
+    fn generalize<T: Into<Term<'tcx>> + Relate<'tcx>>(
+        &self,
+        span: Span,
+        target_vid: impl Into<ty::TermVid>,
+        ambient_variance: ty::Variance,
+        source_term: T,
+    ) -> RelateResult<'tcx, Generalization<T>> {
+        assert!(!source_term.has_escaping_bound_vars());
+        let (for_universe, root_vid) = match target_vid.into() {
+            ty::TermVid::Ty(ty_vid) => (
+                self.probe_ty_var(ty_vid).unwrap_err(),
+                ty::TermVid::Ty(self.inner.borrow_mut().type_variables().sub_root_var(ty_vid)),
+            ),
+            ty::TermVid::Const(ct_vid) => (
+                self.probe_const_var(ct_vid).unwrap_err(),
+                ty::TermVid::Const(
+                    self.inner.borrow_mut().const_unification_table().find(ct_vid).vid,
+                ),
+            ),
+        };
+
+        let mut generalizer = Generalizer {
+            infcx: self,
+            span,
+            root_vid,
+            for_universe,
+            ambient_variance,
+            root_term: source_term.into(),
+            in_alias: false,
+            has_unconstrained_ty_var: false,
+            cache: Default::default(),
+        };
+
+        let value_may_be_infer = generalizer.relate(source_term, source_term)?;
+        let has_unconstrained_ty_var = generalizer.has_unconstrained_ty_var;
+        Ok(Generalization { value_may_be_infer, has_unconstrained_ty_var })
     }
 }
 
@@ -115,18 +260,10 @@ where
 /// establishes `'0: 'x` as a constraint.
 ///
 /// [blog post]: https://is.gd/0hKvIr
-struct Generalizer<'me, 'tcx, D> {
+struct Generalizer<'me, 'tcx> {
     infcx: &'me InferCtxt<'tcx>,
 
-    /// This is used to abstract the behaviors of the three previous
-    /// generalizer-like implementations (`Generalizer`, `TypeGeneralizer`,
-    /// and `ConstInferUnifier`). See [`GeneralizerDelegate`] for more
-    /// information.
-    delegate: &'me mut D,
-
-    /// After we generalize this type, we are going to relate it to
-    /// some other type. What will be the variance at this point?
-    ambient_variance: ty::Variance,
+    span: Span,
 
     /// The vid of the type variable that is in the process of being
     /// instantiated. If we find this within the value we are folding,
@@ -138,6 +275,10 @@ struct Generalizer<'me, 'tcx, D> {
     /// we reject the relation.
     for_universe: ty::UniverseIndex,
 
+    /// After we generalize this type, we are going to relate it to
+    /// some other type. What will be the variance at this point?
+    ambient_variance: ty::Variance,
+
     /// The root term (const or type) we're generalizing. Used for cycle errors.
     root_term: Term<'tcx>,
 
@@ -150,11 +291,11 @@ struct Generalizer<'me, 'tcx, D> {
     /// hold by either normalizing the outer or the inner associated type.
     in_alias: bool,
 
-    /// See the field `needs_wf` in `Generalization`.
-    needs_wf: bool,
+    /// See the field `has_unconstrained_ty_var` in `Generalization`.
+    has_unconstrained_ty_var: bool,
 }
 
-impl<'tcx, D> Generalizer<'_, 'tcx, D> {
+impl<'tcx> Generalizer<'_, 'tcx> {
     /// Create an error that corresponds to the term kind in `root_term`
     fn cyclic_term_error(&self) -> TypeError<'tcx> {
         match self.root_term.unpack() {
@@ -162,12 +303,52 @@ impl<'tcx, D> Generalizer<'_, 'tcx, D> {
             ty::TermKind::Const(ct) => TypeError::CyclicConst(ct),
         }
     }
+
+    /// An occurs check failure inside of an alias does not mean
+    /// that the types definitely don't unify. We may be able
+    /// to normalize the alias after all.
+    ///
+    /// We handle this by lazily equating the alias and generalizing
+    /// it to an inference variable.
+    ///
+    /// This is incomplete and will hopefully soon get fixed by #119106.
+    fn generalize_alias_ty(
+        &mut self,
+        alias: ty::AliasTy<'tcx>,
+    ) -> Result<Ty<'tcx>, TypeError<'tcx>> {
+        let is_nested_alias = mem::replace(&mut self.in_alias, true);
+        let result = match self.relate(alias, alias) {
+            Ok(alias) => Ok(alias.to_ty(self.tcx())),
+            Err(e) => {
+                if is_nested_alias {
+                    return Err(e);
+                } else {
+                    let mut visitor = MaxUniverse::new();
+                    alias.visit_with(&mut visitor);
+                    let infer_replacement_is_complete =
+                        self.for_universe.can_name(visitor.max_universe())
+                            && !alias.has_escaping_bound_vars();
+                    if !infer_replacement_is_complete {
+                        warn!("may incompletely handle alias type: {alias:?}");
+                    }
+
+                    debug!("generalization failure in alias");
+                    Ok(self.infcx.next_ty_var_in_universe(
+                        TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::MiscVariable,
+                            span: self.span,
+                        },
+                        self.for_universe,
+                    ))
+                }
+            }
+        };
+        self.in_alias = is_nested_alias;
+        result
+    }
 }
 
-impl<'tcx, D> TypeRelation<'tcx> for Generalizer<'_, 'tcx, D>
-where
-    D: GeneralizerDelegate<'tcx>,
-{
+impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
     fn tcx(&self) -> TyCtxt<'tcx> {
         self.infcx.tcx
     }
@@ -236,12 +417,6 @@ where
         // subtyping. This is basically our "occurs check", preventing
         // us from creating infinitely sized types.
         let g = match *t.kind() {
-            ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_))
-                if D::forbid_inference_vars() =>
-            {
-                bug!("unexpected inference variable encountered in NLL generalization: {t}");
-            }
-
             ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
                 bug!("unexpected infer type: {t}")
             }
@@ -272,11 +447,10 @@ where
                                     }
                                 }
 
-                                // Bivariant: make a fresh var, but we
-                                // may need a WF predicate. See
-                                // comment on `needs_wf` field for
-                                // more info.
-                                ty::Bivariant => self.needs_wf = true,
+                                // Bivariant: make a fresh var, but remember that
+                                // it is unconstrained. See the comment in
+                                // `Generalization`.
+                                ty::Bivariant => self.has_unconstrained_ty_var = true,
 
                                 // Co/contravariant: this will be
                                 // sufficiently constrained later on.
@@ -318,43 +492,7 @@ where
                 }
             }
 
-            ty::Alias(kind, data) => {
-                // An occurs check failure inside of an alias does not mean
-                // that the types definitely don't unify. We may be able
-                // to normalize the alias after all.
-                //
-                // We handle this by lazily equating the alias and generalizing
-                // it to an inference variable.
-                let is_nested_alias = mem::replace(&mut self.in_alias, true);
-                let result = match self.relate(data, data) {
-                    Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
-                    Err(e) => {
-                        if is_nested_alias {
-                            return Err(e);
-                        } else {
-                            let mut visitor = MaxUniverse::new();
-                            t.visit_with(&mut visitor);
-                            let infer_replacement_is_complete =
-                                self.for_universe.can_name(visitor.max_universe())
-                                    && !t.has_escaping_bound_vars();
-                            if !infer_replacement_is_complete {
-                                warn!("may incompletely handle alias type: {t:?}");
-                            }
-
-                            debug!("generalization failure in alias");
-                            Ok(self.infcx.next_ty_var_in_universe(
-                                TypeVariableOrigin {
-                                    kind: TypeVariableOriginKind::MiscVariable,
-                                    span: self.delegate.span(),
-                                },
-                                self.for_universe,
-                            ))
-                        }
-                    }
-                };
-                self.in_alias = is_nested_alias;
-                result
-            }
+            ty::Alias(_, data) => self.generalize_alias_ty(data),
 
             _ => relate::structurally_relate_tys(self, t, t),
         }?;
@@ -403,7 +541,10 @@ where
             }
         }
 
-        Ok(self.delegate.generalize_region(self.for_universe))
+        Ok(self.infcx.next_region_var_in_universe(
+            RegionVariableOrigin::MiscVariable(self.span),
+            self.for_universe,
+        ))
     }
 
     #[instrument(level = "debug", skip(self, c2), ret)]
@@ -415,9 +556,6 @@ where
         assert_eq!(c, c2); // we are misusing TypeRelation here; both LHS and RHS ought to be ==
 
         match c.kind() {
-            ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
-                bug!("unexpected inference variable encountered in NLL generalization: {:?}", c);
-            }
             ty::ConstKind::Infer(InferConst::Var(vid)) => {
                 // If root const vids are equal, then `root_vid` and
                 // `vid` are related and we'd be inferring an infinitely
@@ -452,6 +590,9 @@ where
                 }
             }
             ty::ConstKind::Infer(InferConst::EffectVar(_)) => Ok(c),
+            // FIXME: Unevaluated constants are also not rigid, so the current
+            // approach of always relating them structurally is incomplete.
+            //
             // FIXME: remove this branch once `structurally_relate_consts` is fully
             // structural.
             ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args }) => {
@@ -511,30 +652,27 @@ pub struct Generalization<T> {
     /// recursion.
     pub value_may_be_infer: T,
 
-    /// If true, then the generalized type may not be well-formed,
-    /// even if the source type is well-formed, so we should add an
-    /// additional check to enforce that it is. This arises in
-    /// particular around 'bivariant' type parameters that are only
-    /// constrained by a where-clause. As an example, imagine a type:
+    /// In general, we do not check whether all types which occur during
+    /// type checking are well-formed. We only check wf of user-provided types
+    /// and when actually using a type, e.g. for method calls.
+    ///
+    /// This means that when subtyping, we may end up with unconstrained
+    /// inference variables if a generalized type has bivariant parameters.
+    /// A parameter may only be bivariant if it is constrained by a projection
+    /// bound in a where-clause. As an example, imagine a type:
     ///
     ///     struct Foo<A, B> where A: Iterator<Item = B> {
     ///         data: A
     ///     }
     ///
-    /// here, `A` will be covariant, but `B` is
-    /// unconstrained. However, whatever it is, for `Foo` to be WF, it
-    /// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`,
-    /// then after generalization we will wind up with a type like
-    /// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C,
-    /// ?D>` (or `>:`), we will wind up with the requirement that `?A
-    /// <: ?C`, but no particular relationship between `?B` and `?D`
-    /// (after all, we do not know the variance of the normalized form
-    /// of `A::Item` with respect to `A`). If we do nothing else, this
-    /// may mean that `?D` goes unconstrained (as in #41677). So, in
-    /// this scenario where we create a new type variable in a
-    /// bivariant context, we set the `needs_wf` flag to true. This
-    /// will force the calling code to check that `WF(Foo<?C, ?D>)`
-    /// holds, which in turn implies that `?C::Item == ?D`. So once
-    /// `?C` is constrained, that should suffice to restrict `?D`.
-    pub needs_wf: bool,
+    /// here, `A` will be covariant, but `B` is unconstrained.
+    ///
+    /// However, whatever it is, for `Foo` to be WF, it must be equal to `A::Item`.
+    /// If we have an input `Foo<?A, ?B>`, then after generalization we will wind
+    /// up with a type like `Foo<?C, ?D>`. When we enforce `Foo<?A, ?B> <: Foo<?C, ?D>`,
+    /// we will wind up with the requirement that `?A <: ?C`, but no particular
+    /// relationship between `?B` and `?D` (after all, these types may be completely
+    /// different). If we do nothing else, this may mean that `?D` goes unconstrained
+    /// (as in #41677). To avoid this we emit a `WellFormed` obligation in these cases.
+    pub has_unconstrained_ty_var: bool,
 }
diff --git a/compiler/rustc_infer/src/infer/relate/glb.rs b/compiler/rustc_infer/src/infer/relate/glb.rs
index aa89124301e..6cf51354599 100644
--- a/compiler/rustc_infer/src/infer/relate/glb.rs
+++ b/compiler/rustc_infer/src/infer/relate/glb.rs
@@ -2,6 +2,7 @@
 
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::Span;
 
 use super::combine::{CombineFields, ObligationEmittingRelation};
 use super::lattice::{self, LatticeDir};
@@ -134,6 +135,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
 }
 
 impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
+    fn span(&self) -> Span {
+        self.fields.trace.span()
+    }
+
     fn param_env(&self) -> ty::ParamEnv<'tcx> {
         self.fields.param_env
     }
diff --git a/compiler/rustc_infer/src/infer/relate/lub.rs b/compiler/rustc_infer/src/infer/relate/lub.rs
index 87d777530c8..5b4f80fd73a 100644
--- a/compiler/rustc_infer/src/infer/relate/lub.rs
+++ b/compiler/rustc_infer/src/infer/relate/lub.rs
@@ -7,6 +7,7 @@ use crate::traits::{ObligationCause, PredicateObligations};
 
 use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
+use rustc_span::Span;
 
 /// "Least upper bound" (common supertype)
 pub struct Lub<'combine, 'infcx, 'tcx> {
@@ -134,6 +135,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
 }
 
 impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
+    fn span(&self) -> Span {
+        self.fields.trace.span()
+    }
+
     fn param_env(&self) -> ty::ParamEnv<'tcx> {
         self.fields.param_env
     }
diff --git a/compiler/rustc_infer/src/infer/relate/mod.rs b/compiler/rustc_infer/src/infer/relate/mod.rs
index f688c2b74a6..1207377e857 100644
--- a/compiler/rustc_infer/src/infer/relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/relate/mod.rs
@@ -3,10 +3,9 @@
 
 pub(super) mod combine;
 mod equate;
-pub(super) mod generalize;
+mod generalize;
 mod glb;
 mod higher_ranked;
 mod lattice;
 mod lub;
-pub mod nll;
 mod sub;
diff --git a/compiler/rustc_infer/src/infer/relate/nll.rs b/compiler/rustc_infer/src/infer/relate/nll.rs
deleted file mode 100644
index 5e2d2af9b85..00000000000
--- a/compiler/rustc_infer/src/infer/relate/nll.rs
+++ /dev/null
@@ -1,720 +0,0 @@
-//! This code is kind of an alternate way of doing subtyping,
-//! supertyping, and type equating, distinct from the `combine.rs`
-//! code but very similar in its effect and design. Eventually the two
-//! ought to be merged. This code is intended for use in NLL and chalk.
-//!
-//! Here are the key differences:
-//!
-//! - This code may choose to bypass some checks (e.g., the occurs check)
-//!   in the case where we know that there are no unbound type inference
-//!   variables. This is the case for NLL, because at NLL time types are fully
-//!   inferred up-to regions.
-//! - This code uses "universes" to handle higher-ranked regions and
-//!   not the leak-check. This is "more correct" than what rustc does
-//!   and we are generally migrating in this direction, but NLL had to
-//!   get there first.
-//!
-//! Also, this code assumes that there are no bound types at all, not even
-//! free ones. This is ok because:
-//! - we are not relating anything quantified over some type variable
-//! - we will have instantiated all the bound type vars already (the one
-//!   thing we relate in chalk are basically domain goals and their
-//!   constituents)
-
-use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::traits::ObligationCause;
-use rustc_middle::ty::fold::FnMutDelegate;
-use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
-use rustc_span::{Span, Symbol};
-use std::fmt::Debug;
-
-use super::combine::ObligationEmittingRelation;
-use super::generalize::{self, Generalization};
-use crate::infer::InferCtxt;
-use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
-use crate::traits::{Obligation, PredicateObligations};
-
-pub struct TypeRelating<'me, 'tcx, D>
-where
-    D: TypeRelatingDelegate<'tcx>,
-{
-    infcx: &'me InferCtxt<'tcx>,
-
-    /// Callback to use when we deduce an outlives relationship.
-    delegate: D,
-
-    /// How are we relating `a` and `b`?
-    ///
-    /// - Covariant means `a <: b`.
-    /// - Contravariant means `b <: a`.
-    /// - Invariant means `a == b`.
-    /// - Bivariant means that it doesn't matter.
-    ambient_variance: ty::Variance,
-
-    ambient_variance_info: ty::VarianceDiagInfo<'tcx>,
-}
-
-pub trait TypeRelatingDelegate<'tcx> {
-    fn param_env(&self) -> ty::ParamEnv<'tcx>;
-    fn span(&self) -> Span;
-
-    /// Push a constraint `sup: sub` -- this constraint must be
-    /// satisfied for the two types to be related. `sub` and `sup` may
-    /// be regions from the type or new variables created through the
-    /// delegate.
-    fn push_outlives(
-        &mut self,
-        sup: ty::Region<'tcx>,
-        sub: ty::Region<'tcx>,
-        info: ty::VarianceDiagInfo<'tcx>,
-    );
-
-    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);
-
-    /// Creates a new universe index. Used when instantiating placeholders.
-    fn create_next_universe(&mut self) -> ty::UniverseIndex;
-
-    /// Creates a new region variable representing a higher-ranked
-    /// region that is instantiated existentially. This creates an
-    /// inference variable, typically.
-    ///
-    /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then
-    /// we will invoke this method to instantiate `'a` with an
-    /// inference variable (though `'b` would be instantiated first,
-    /// as a placeholder).
-    fn next_existential_region_var(
-        &mut self,
-        was_placeholder: bool,
-        name: Option<Symbol>,
-    ) -> ty::Region<'tcx>;
-
-    /// Creates a new region variable representing a
-    /// higher-ranked region that is instantiated universally.
-    /// This creates a new region placeholder, typically.
-    ///
-    /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then
-    /// we will invoke this method to instantiate `'b` with a
-    /// placeholder region.
-    fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>;
-
-    /// Creates a new existential region in the given universe. This
-    /// is used when handling subtyping and type variables -- if we
-    /// have that `?X <: Foo<'a>`, for example, we would instantiate
-    /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh
-    /// existential variable created by this function. We would then
-    /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives
-    /// relation stating that `'?0: 'a`).
-    fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
-
-    /// Enables some optimizations if we do not expect inference variables
-    /// in the RHS of the relation.
-    fn forbid_inference_vars() -> bool;
-}
-
-impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D>
-where
-    D: TypeRelatingDelegate<'tcx>,
-{
-    pub fn new(infcx: &'me InferCtxt<'tcx>, delegate: D, ambient_variance: ty::Variance) -> Self {
-        Self {
-            infcx,
-            delegate,
-            ambient_variance,
-            ambient_variance_info: ty::VarianceDiagInfo::default(),
-        }
-    }
-
-    fn ambient_covariance(&self) -> bool {
-        match self.ambient_variance {
-            ty::Variance::Covariant | ty::Variance::Invariant => true,
-            ty::Variance::Contravariant | ty::Variance::Bivariant => false,
-        }
-    }
-
-    fn ambient_contravariance(&self) -> bool {
-        match self.ambient_variance {
-            ty::Variance::Contravariant | ty::Variance::Invariant => true,
-            ty::Variance::Covariant | ty::Variance::Bivariant => false,
-        }
-    }
-
-    /// Push a new outlives requirement into our output set of
-    /// constraints.
-    fn push_outlives(
-        &mut self,
-        sup: ty::Region<'tcx>,
-        sub: ty::Region<'tcx>,
-        info: ty::VarianceDiagInfo<'tcx>,
-    ) {
-        debug!("push_outlives({:?}: {:?})", sup, sub);
-
-        self.delegate.push_outlives(sup, sub, info);
-    }
-
-    /// Relate a type inference variable with a value type. This works
-    /// by creating a "generalization" G of the value where all the
-    /// lifetimes are replaced with fresh inference values. This
-    /// generalization G becomes the value of the inference variable,
-    /// and is then related in turn to the value. So e.g. if you had
-    /// `vid = ?0` and `value = &'a u32`, we might first instantiate
-    /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable,
-    /// and then relate `&'0 u32` with `&'a u32` (resulting in
-    /// relations between `'0` and `'a`).
-    ///
-    /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)`
-    /// -- in other words, it is always an (unresolved) inference
-    /// variable `vid` and a type `ty` that are being related, but the
-    /// vid may appear either as the "a" type or the "b" type,
-    /// depending on where it appears in the tuple. The trait
-    /// `VidValuePair` lets us work with the vid/type while preserving
-    /// the "sidedness" when necessary -- the sidedness is relevant in
-    /// particular for the variance and set of in-scope things.
-    fn relate_ty_var<PAIR: VidValuePair<'tcx>>(
-        &mut self,
-        pair: PAIR,
-    ) -> RelateResult<'tcx, Ty<'tcx>> {
-        debug!("relate_ty_var({:?})", pair);
-
-        let vid = pair.vid();
-        let value_ty = pair.value_ty();
-
-        // FIXME(invariance) -- this logic assumes invariance, but that is wrong.
-        // This only presently applies to chalk integration, as NLL
-        // doesn't permit type variables to appear on both sides (and
-        // doesn't use lazy norm).
-        match *value_ty.kind() {
-            ty::Infer(ty::TyVar(value_vid)) => {
-                // Two type variables: just equate them.
-                self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid);
-                return Ok(value_ty);
-            }
-
-            _ => (),
-        }
-
-        let generalized_ty = self.generalize(value_ty, vid)?;
-        debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty);
-
-        if D::forbid_inference_vars() {
-            // In NLL, we don't have type inference variables
-            // floating around, so we can do this rather imprecise
-            // variant of the occurs-check.
-            assert!(!generalized_ty.has_non_region_infer());
-        }
-
-        self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty);
-
-        // Relate the generalized kind to the original one.
-        let result = pair.relate_generalized_ty(self, generalized_ty);
-
-        debug!("relate_ty_var: complete, result = {:?}", result);
-        result
-    }
-
-    fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
-        let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize(
-            self.infcx,
-            &mut self.delegate,
-            ty,
-            for_vid,
-            self.ambient_variance,
-        )?;
-
-        if ty.is_ty_var() {
-            span_bug!(self.delegate.span(), "occurs check failure in MIR typeck");
-        }
-        Ok(ty)
-    }
-
-    fn relate_opaques(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
-        let mut generalize = |ty, ty_is_expected| {
-            let var = self.infcx.next_ty_var_id_in_universe(
-                TypeVariableOrigin {
-                    kind: TypeVariableOriginKind::MiscVariable,
-                    span: self.delegate.span(),
-                },
-                ty::UniverseIndex::ROOT,
-            );
-            if ty_is_expected {
-                self.relate_ty_var((ty, var))
-            } else {
-                self.relate_ty_var((var, ty))
-            }
-        };
-        let (a, b) = match (a.kind(), b.kind()) {
-            (&ty::Alias(ty::Opaque, ..), _) => (a, generalize(b, false)?),
-            (_, &ty::Alias(ty::Opaque, ..)) => (generalize(a, true)?, b),
-            _ => unreachable!(
-                "expected at least one opaque type in `relate_opaques`, got {a} and {b}."
-            ),
-        };
-        let cause = ObligationCause::dummy_with_span(self.delegate.span());
-        let obligations = self
-            .infcx
-            .handle_opaque_type(a, b, true, &cause, self.delegate.param_env())?
-            .obligations;
-        self.delegate.register_obligations(obligations);
-        trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated");
-        Ok(a)
-    }
-
-    fn enter_forall<T, U>(
-        &mut self,
-        binder: ty::Binder<'tcx, T>,
-        f: impl FnOnce(&mut Self, T) -> U,
-    ) -> U
-    where
-        T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
-    {
-        let value = if let Some(inner) = binder.no_bound_vars() {
-            inner
-        } else {
-            let mut next_region = {
-                let nll_delegate = &mut self.delegate;
-                let mut lazy_universe = None;
-
-                move |br: ty::BoundRegion| {
-                    // The first time this closure is called, create a
-                    // new universe for the placeholders we will make
-                    // from here out.
-                    let universe = lazy_universe.unwrap_or_else(|| {
-                        let universe = nll_delegate.create_next_universe();
-                        lazy_universe = Some(universe);
-                        universe
-                    });
-
-                    let placeholder = ty::PlaceholderRegion { universe, bound: br };
-                    debug!(?placeholder);
-                    let placeholder_reg = nll_delegate.next_placeholder_region(placeholder);
-                    debug!(?placeholder_reg);
-
-                    placeholder_reg
-                }
-            };
-
-            let delegate = FnMutDelegate {
-                regions: &mut next_region,
-                types: &mut |_bound_ty: ty::BoundTy| {
-                    unreachable!("we only replace regions in nll_relate, not types")
-                },
-                consts: &mut |_bound_var: ty::BoundVar, _ty| {
-                    unreachable!("we only replace regions in nll_relate, not consts")
-                },
-            };
-
-            self.infcx.tcx.replace_bound_vars_uncached(binder, delegate)
-        };
-
-        debug!(?value);
-        f(self, value)
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn instantiate_binder_with_existentials<T>(&mut self, binder: ty::Binder<'tcx, T>) -> T
-    where
-        T: ty::TypeFoldable<TyCtxt<'tcx>> + Copy,
-    {
-        if let Some(inner) = binder.no_bound_vars() {
-            return inner;
-        }
-
-        let mut next_region = {
-            let nll_delegate = &mut self.delegate;
-            let mut reg_map = FxHashMap::default();
-
-            move |br: ty::BoundRegion| {
-                if let Some(ex_reg_var) = reg_map.get(&br) {
-                    return *ex_reg_var;
-                } else {
-                    let ex_reg_var =
-                        nll_delegate.next_existential_region_var(true, br.kind.get_name());
-                    debug!(?ex_reg_var);
-                    reg_map.insert(br, ex_reg_var);
-
-                    ex_reg_var
-                }
-            }
-        };
-
-        let delegate = FnMutDelegate {
-            regions: &mut next_region,
-            types: &mut |_bound_ty: ty::BoundTy| {
-                unreachable!("we only replace regions in nll_relate, not types")
-            },
-            consts: &mut |_bound_var: ty::BoundVar, _ty| {
-                unreachable!("we only replace regions in nll_relate, not consts")
-            },
-        };
-
-        let replaced = self.infcx.tcx.replace_bound_vars_uncached(binder, delegate);
-        debug!(?replaced);
-
-        replaced
-    }
-}
-
-/// When we instantiate an inference variable with a value in
-/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`,
-/// but the ordering may vary (depending on whether the inference
-/// variable was found on the `a` or `b` sides). Therefore, this trait
-/// allows us to factor out common code, while preserving the order
-/// when needed.
-trait VidValuePair<'tcx>: Debug {
-    /// Extract the inference variable (which could be either the
-    /// first or second part of the tuple).
-    fn vid(&self) -> ty::TyVid;
-
-    /// Extract the value it is being related to (which will be the
-    /// opposite part of the tuple from the vid).
-    fn value_ty(&self) -> Ty<'tcx>;
-
-    /// Given a generalized type G that should replace the vid, relate
-    /// G to the value, putting G on whichever side the vid would have
-    /// appeared.
-    fn relate_generalized_ty<D>(
-        &self,
-        relate: &mut TypeRelating<'_, 'tcx, D>,
-        generalized_ty: Ty<'tcx>,
-    ) -> RelateResult<'tcx, Ty<'tcx>>
-    where
-        D: TypeRelatingDelegate<'tcx>;
-}
-
-impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) {
-    fn vid(&self) -> ty::TyVid {
-        self.0
-    }
-
-    fn value_ty(&self) -> Ty<'tcx> {
-        self.1
-    }
-
-    fn relate_generalized_ty<D>(
-        &self,
-        relate: &mut TypeRelating<'_, 'tcx, D>,
-        generalized_ty: Ty<'tcx>,
-    ) -> RelateResult<'tcx, Ty<'tcx>>
-    where
-        D: TypeRelatingDelegate<'tcx>,
-    {
-        relate.relate(generalized_ty, self.value_ty())
-    }
-}
-
-// In this case, the "vid" is the "b" type.
-impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) {
-    fn vid(&self) -> ty::TyVid {
-        self.1
-    }
-
-    fn value_ty(&self) -> Ty<'tcx> {
-        self.0
-    }
-
-    fn relate_generalized_ty<D>(
-        &self,
-        relate: &mut TypeRelating<'_, 'tcx, D>,
-        generalized_ty: Ty<'tcx>,
-    ) -> RelateResult<'tcx, Ty<'tcx>>
-    where
-        D: TypeRelatingDelegate<'tcx>,
-    {
-        relate.relate(self.value_ty(), generalized_ty)
-    }
-}
-
-impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D>
-where
-    D: TypeRelatingDelegate<'tcx>,
-{
-    fn tcx(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn tag(&self) -> &'static str {
-        "nll::subtype"
-    }
-
-    fn a_is_expected(&self) -> bool {
-        true
-    }
-
-    #[instrument(skip(self, info), level = "trace", ret)]
-    fn relate_with_variance<T: Relate<'tcx>>(
-        &mut self,
-        variance: ty::Variance,
-        info: ty::VarianceDiagInfo<'tcx>,
-        a: T,
-        b: T,
-    ) -> RelateResult<'tcx, T> {
-        let old_ambient_variance = self.ambient_variance;
-        self.ambient_variance = self.ambient_variance.xform(variance);
-        self.ambient_variance_info = self.ambient_variance_info.xform(info);
-
-        debug!(?self.ambient_variance);
-        // In a bivariant context this always succeeds.
-        let r =
-            if self.ambient_variance == ty::Variance::Bivariant { a } else { self.relate(a, b)? };
-
-        self.ambient_variance = old_ambient_variance;
-
-        Ok(r)
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
-        let infcx = self.infcx;
-
-        let a = self.infcx.shallow_resolve(a);
-
-        if !D::forbid_inference_vars() {
-            b = self.infcx.shallow_resolve(b);
-        }
-
-        if a == b {
-            return Ok(a);
-        }
-
-        match (a.kind(), b.kind()) {
-            (_, &ty::Infer(ty::TyVar(vid))) => {
-                if D::forbid_inference_vars() {
-                    // Forbid inference variables in the RHS.
-                    bug!("unexpected inference var {:?}", b)
-                } else {
-                    self.relate_ty_var((a, vid))
-                }
-            }
-
-            (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)),
-
-            (
-                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: a_def_id, .. }),
-                &ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }),
-            ) if a_def_id == b_def_id || infcx.next_trait_solver() => {
-                infcx.super_combine_tys(self, a, b).or_else(|err| {
-                    // This behavior is only there for the old solver, the new solver
-                    // shouldn't ever fail. Instead, it unconditionally emits an
-                    // alias-relate goal.
-                    assert!(!self.infcx.next_trait_solver());
-                    self.tcx().dcx().span_delayed_bug(
-                        self.delegate.span(),
-                        "failure to relate an opaque to itself should result in an error later on",
-                    );
-                    if a_def_id.is_local() { self.relate_opaques(a, b) } else { Err(err) }
-                })
-            }
-            (&ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }), _)
-            | (_, &ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }))
-                if def_id.is_local() && !self.infcx.next_trait_solver() =>
-            {
-                self.relate_opaques(a, b)
-            }
-
-            _ => {
-                debug!(?a, ?b, ?self.ambient_variance);
-
-                // Will also handle unification of `IntVar` and `FloatVar`.
-                self.infcx.super_combine_tys(self, a, b)
-            }
-        }
-    }
-
-    #[instrument(skip(self), level = "trace")]
-    fn regions(
-        &mut self,
-        a: ty::Region<'tcx>,
-        b: ty::Region<'tcx>,
-    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
-        debug!(?self.ambient_variance);
-
-        if self.ambient_covariance() {
-            // Covariant: &'a u8 <: &'b u8. Hence, `'a: 'b`.
-            self.push_outlives(a, b, self.ambient_variance_info);
-        }
-
-        if self.ambient_contravariance() {
-            // Contravariant: &'b u8 <: &'a u8. Hence, `'b: 'a`.
-            self.push_outlives(b, a, self.ambient_variance_info);
-        }
-
-        Ok(a)
-    }
-
-    fn consts(
-        &mut self,
-        a: ty::Const<'tcx>,
-        mut b: ty::Const<'tcx>,
-    ) -> RelateResult<'tcx, ty::Const<'tcx>> {
-        let a = self.infcx.shallow_resolve(a);
-
-        if !D::forbid_inference_vars() {
-            b = self.infcx.shallow_resolve(b);
-        }
-
-        match b.kind() {
-            ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => {
-                // Forbid inference variables in the RHS.
-                self.infcx.dcx().span_delayed_bug(
-                    self.delegate.span(),
-                    format!("unexpected inference var {b:?}",),
-                );
-                Ok(a)
-            }
-            // FIXME(invariance): see the related FIXME above.
-            _ => self.infcx.super_combine_consts(self, a, b),
-        }
-    }
-
-    #[instrument(skip(self), level = "trace")]
-    fn binders<T>(
-        &mut self,
-        a: ty::Binder<'tcx, T>,
-        b: ty::Binder<'tcx, T>,
-    ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>
-    where
-        T: Relate<'tcx>,
-    {
-        // We want that
-        //
-        // ```
-        // for<'a> fn(&'a u32) -> &'a u32 <:
-        //   fn(&'b u32) -> &'b u32
-        // ```
-        //
-        // but not
-        //
-        // ```
-        // fn(&'a u32) -> &'a u32 <:
-        //   for<'b> fn(&'b u32) -> &'b u32
-        // ```
-        //
-        // We therefore proceed as follows:
-        //
-        // - Instantiate binders on `b` universally, yielding a universe U1.
-        // - Instantiate binders on `a` existentially in U1.
-
-        debug!(?self.ambient_variance);
-
-        if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) {
-            // Fast path for the common case.
-            self.relate(a, b)?;
-            return Ok(ty::Binder::dummy(a));
-        }
-
-        if self.ambient_covariance() {
-            // Covariance, so we want `for<..> A <: for<..> B` --
-            // therefore we compare any instantiation of A (i.e., A
-            // instantiated with existentials) against every
-            // instantiation of B (i.e., B instantiated with
-            // universals).
-
-            // Reset the ambient variance to covariant. This is needed
-            // to correctly handle cases like
-            //
-            //     for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32)
-            //
-            // Somewhat surprisingly, these two types are actually
-            // **equal**, even though the one on the right looks more
-            // polymorphic. The reason is due to subtyping. To see it,
-            // consider that each function can call the other:
-            //
-            // - The left function can call the right with `'b` and
-            //   `'c` both equal to `'a`
-            //
-            // - The right function can call the left with `'a` set to
-            //   `{P}`, where P is the point in the CFG where the call
-            //   itself occurs. Note that `'b` and `'c` must both
-            //   include P. At the point, the call works because of
-            //   subtyping (i.e., `&'b u32 <: &{P} u32`).
-            let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant);
-
-            // Note: the order here is important. Create the placeholders first, otherwise
-            // we assign the wrong universe to the existential!
-            self.enter_forall(b, |this, b| {
-                let a = this.instantiate_binder_with_existentials(a);
-                this.relate(a, b)
-            })?;
-
-            self.ambient_variance = variance;
-        }
-
-        if self.ambient_contravariance() {
-            // Contravariance, so we want `for<..> A :> for<..> B`
-            // -- therefore we compare every instantiation of A (i.e.,
-            // A instantiated with universals) against any
-            // instantiation of B (i.e., B instantiated with
-            // existentials). Opposite of above.
-
-            // Reset ambient variance to contravariance. See the
-            // covariant case above for an explanation.
-            let variance =
-                std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
-
-            self.enter_forall(a, |this, a| {
-                let b = this.instantiate_binder_with_existentials(b);
-                this.relate(a, b)
-            })?;
-
-            self.ambient_variance = variance;
-        }
-
-        Ok(a)
-    }
-}
-
-impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D>
-where
-    D: TypeRelatingDelegate<'tcx>,
-{
-    fn param_env(&self) -> ty::ParamEnv<'tcx> {
-        self.delegate.param_env()
-    }
-
-    fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
-        self.delegate.register_obligations(
-            obligations
-                .into_iter()
-                .map(|to_pred| {
-                    Obligation::new(self.tcx(), ObligationCause::dummy(), self.param_env(), to_pred)
-                })
-                .collect(),
-        );
-    }
-
-    fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>) {
-        self.delegate.register_obligations(obligations);
-    }
-
-    fn alias_relate_direction(&self) -> ty::AliasRelationDirection {
-        unreachable!("manually overridden to handle ty::Variance::Contravariant ambient variance")
-    }
-
-    fn register_type_relate_obligation(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) {
-        self.register_predicates([ty::Binder::dummy(match self.ambient_variance {
-            ty::Variance::Covariant => ty::PredicateKind::AliasRelate(
-                a.into(),
-                b.into(),
-                ty::AliasRelationDirection::Subtype,
-            ),
-            // a :> b is b <: a
-            ty::Variance::Contravariant => ty::PredicateKind::AliasRelate(
-                b.into(),
-                a.into(),
-                ty::AliasRelationDirection::Subtype,
-            ),
-            ty::Variance::Invariant => ty::PredicateKind::AliasRelate(
-                a.into(),
-                b.into(),
-                ty::AliasRelationDirection::Equate,
-            ),
-            // FIXME(deferred_projection_equality): Implement this when we trigger it.
-            // Probably just need to do nothing here.
-            ty::Variance::Bivariant => {
-                unreachable!("cannot defer an alias-relate goal with Bivariant variance (yet?)")
-            }
-        })]);
-    }
-}
diff --git a/compiler/rustc_infer/src/infer/relate/sub.rs b/compiler/rustc_infer/src/infer/relate/sub.rs
index 36876acd7c0..5bd3a238a67 100644
--- a/compiler/rustc_infer/src/infer/relate/sub.rs
+++ b/compiler/rustc_infer/src/infer/relate/sub.rs
@@ -6,6 +6,7 @@ use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::visit::TypeVisitableExt;
 use rustc_middle::ty::TyVar;
 use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
 use std::mem;
 
 /// Ensures `a` is made a subtype of `b`. Returns `a` on success.
@@ -103,12 +104,12 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
 
                 Ok(a)
             }
-            (&ty::Infer(TyVar(a_id)), _) => {
-                self.fields.instantiate(b, ty::Contravariant, a_id, !self.a_is_expected)?;
+            (&ty::Infer(TyVar(a_vid)), _) => {
+                infcx.instantiate_ty_var(self, self.a_is_expected, a_vid, ty::Covariant, b)?;
                 Ok(a)
             }
-            (_, &ty::Infer(TyVar(b_id))) => {
-                self.fields.instantiate(a, ty::Covariant, b_id, self.a_is_expected)?;
+            (_, &ty::Infer(TyVar(b_vid))) => {
+                infcx.instantiate_ty_var(self, !self.a_is_expected, b_vid, ty::Contravariant, a)?;
                 Ok(a)
             }
 
@@ -199,6 +200,10 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
 }
 
 impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> {
+    fn span(&self) -> Span {
+        self.fields.trace.span()
+    }
+
     fn param_env(&self) -> ty::ParamEnv<'tcx> {
         self.fields.param_env
     }
diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml
index a238eacda44..0e90836145e 100644
--- a/compiler/rustc_interface/Cargo.toml
+++ b/compiler/rustc_interface/Cargo.toml
@@ -5,7 +5,6 @@ edition = "2021"
 
 [dependencies]
 # tidy-alphabetical-start
-libloading = "0.8.0"
 rustc-rayon = { version = "0.5.0", optional = true }
 rustc-rayon-core = { version = "0.5.0", optional = true }
 rustc_ast = { path = "../rustc_ast" }
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 8a4705e0056..cd7957c3bce 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -423,18 +423,43 @@ pub fn run_compiler<R: Send>(config: Config, f: impl FnOnce(&Compiler) -> R + Se
                 Compiler { sess, codegen_backend, override_queries: config.override_queries };
 
             rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || {
-                let r = {
-                    let _sess_abort_error = defer(|| {
-                        compiler.sess.finish_diagnostics(&config.registry);
+                // There are two paths out of `f`.
+                // - Normal exit.
+                // - Panic, e.g. triggered by `abort_if_errors`.
+                //
+                // We must run `finish_diagnostics` in both cases.
+                let res = {
+                    // If `f` panics, `finish_diagnostics` will run during
+                    // unwinding because of the `defer`.
+                    let mut guar = None;
+                    let sess_abort_guard = defer(|| {
+                        guar = compiler.sess.finish_diagnostics(&config.registry);
                     });
 
-                    f(&compiler)
+                    let res = f(&compiler);
+
+                    // If `f` doesn't panic, `finish_diagnostics` will run
+                    // normally when `sess_abort_guard` is dropped.
+                    drop(sess_abort_guard);
+
+                    // If `finish_diagnostics` emits errors (e.g. stashed
+                    // errors) we can't return an error directly, because the
+                    // return type of this function is `R`, not `Result<R, E>`.
+                    // But we need to communicate the errors' existence to the
+                    // caller, otherwise the caller might mistakenly think that
+                    // no errors occurred and return a zero exit code. So we
+                    // abort (panic) instead, similar to if `f` had panicked.
+                    if guar.is_some() {
+                        compiler.sess.dcx().abort_if_errors();
+                    }
+
+                    res
                 };
 
                 let prof = compiler.sess.prof.clone();
-
                 prof.generic_activity("drop_compiler").run(move || drop(compiler));
-                r
+
+                res
             })
         },
     )
diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs
index 24c2e290534..d0ce23dacb5 100644
--- a/compiler/rustc_interface/src/lib.rs
+++ b/compiler/rustc_interface/src/lib.rs
@@ -1,5 +1,4 @@
 #![feature(decl_macro)]
-#![feature(error_iter)]
 #![feature(generic_nonzero)]
 #![feature(lazy_cell)]
 #![feature(let_chains)]
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 60d13f02ad7..66140168759 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
     // lot of annoying errors in the ui tests (basically,
     // lint warnings and so on -- kindck used to do this abort, but
     // kindck is gone now). -nmatsakis
-    if let Some(reported) = sess.dcx().has_errors() {
-        return Err(reported);
-    } else if sess.dcx().stashed_err_count() > 0 {
-        // Without this case we sometimes get delayed bug ICEs and I don't
-        // understand why. -nnethercote
-        return Err(sess.dcx().delayed_bug("some stashed error is waiting for use"));
+    //
+    // But we exclude lint errors from this, because lint errors are typically
+    // less serious and we're more likely to want to continue (#87337).
+    if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() {
+        return Err(guar);
     }
 
     sess.time("misc_checking_3", || {
@@ -937,9 +936,7 @@ pub fn start_codegen<'tcx>(
 
     if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
         if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
-            let dcx = tcx.dcx();
-            dcx.emit_err(errors::CantEmitMIR { error });
-            dcx.abort_if_errors();
+            tcx.dcx().emit_fatal(errors::CantEmitMIR { error });
         }
     }
 
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 211bcb9da94..86858bfe41d 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> {
 
     pub fn codegen_and_build_linker(&'tcx self) -> Result<Linker> {
         self.global_ctxt()?.enter(|tcx| {
-            // Don't do code generation if there were any errors
-            self.compiler.sess.compile_status()?;
-
-            // If we have any delayed bugs, for example because we created TyKind::Error earlier,
-            // it's likely that codegen will only cause more ICEs, obscuring the original problem
-            self.compiler.sess.dcx().flush_delayed();
+            // Don't do code generation if there were any errors. Likewise if
+            // there were any delayed bugs, because codegen will likely cause
+            // more ICEs, obscuring the original problem.
+            if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() {
+                return Err(guar);
+            }
 
             // Hook for UI tests.
             Self::check_for_rustc_errors_attr(tcx);
@@ -261,7 +261,9 @@ impl Linker {
         let (codegen_results, work_products) =
             codegen_backend.join_codegen(self.ongoing_codegen, sess, &self.output_filenames);
 
-        sess.compile_status()?;
+        if let Some(guar) = sess.dcx().has_errors() {
+            return Err(guar);
+        }
 
         sess.time("serialize_work_products", || {
             rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products)
diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs
index 087c43075f1..823614e1f06 100644
--- a/compiler/rustc_interface/src/util.rs
+++ b/compiler/rustc_interface/src/util.rs
@@ -1,10 +1,10 @@
 use crate::errors;
 use info;
-use libloading::Library;
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 #[cfg(parallel_compiler)]
 use rustc_data_structures::sync;
+use rustc_metadata::{load_symbol_from_dylib, DylibError};
 use rustc_parse::validate_attr;
 use rustc_session as session;
 use rustc_session::config::{self, Cfg, CrateType, OutFileName, OutputFilenames, OutputTypes};
@@ -17,7 +17,6 @@ use rustc_span::symbol::{sym, Symbol};
 use session::EarlyDiagCtxt;
 use std::env;
 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
-use std::mem;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicBool, Ordering};
 use std::sync::OnceLock;
@@ -162,29 +161,19 @@ pub(crate) fn run_in_thread_pool_with_globals<F: FnOnce() -> R + Send, R: Send>(
 }
 
 fn load_backend_from_dylib(early_dcx: &EarlyDiagCtxt, path: &Path) -> MakeBackendFn {
-    fn format_err(e: &(dyn std::error::Error + 'static)) -> String {
-        e.sources().map(|e| format!(": {e}")).collect()
-    }
-    let lib = unsafe { Library::new(path) }.unwrap_or_else(|err| {
-        let err = format!("couldn't load codegen backend {path:?}{}", format_err(&err));
-        early_dcx.early_fatal(err);
-    });
-
-    let backend_sym = unsafe { lib.get::<MakeBackendFn>(b"__rustc_codegen_backend") }
-        .unwrap_or_else(|e| {
+    match unsafe { load_symbol_from_dylib::<MakeBackendFn>(path, "__rustc_codegen_backend") } {
+        Ok(backend_sym) => backend_sym,
+        Err(DylibError::DlOpen(path, err)) => {
+            let err = format!("couldn't load codegen backend {path}{err}");
+            early_dcx.early_fatal(err);
+        }
+        Err(DylibError::DlSym(_path, err)) => {
             let e = format!(
-                "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{}",
-                format_err(&e)
+                "`__rustc_codegen_backend` symbol lookup in the codegen backend failed{err}",
             );
             early_dcx.early_fatal(e);
-        });
-
-    // Intentionally leak the dynamic library. We can't ever unload it
-    // since the library can make things that will live arbitrarily long.
-    let backend_sym = unsafe { backend_sym.into_raw() };
-    mem::forget(lib);
-
-    *backend_sym
+        }
+    }
 }
 
 /// Get the codegen backend based on the name and specified sysroot.
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 785895e0ab8..1548646c04a 100644
--- a/compiler/rustc_lint/messages.ftl
+++ b/compiler/rustc_lint/messages.ftl
@@ -72,8 +72,11 @@ lint_builtin_explicit_outlives = outlives requirements can be inferred
 
 lint_builtin_export_name_fn = declaration of a function with `export_name`
 lint_builtin_export_name_method = declaration of a method with `export_name`
-
 lint_builtin_export_name_static = declaration of a static with `export_name`
+
+lint_builtin_global_asm = usage of `core::arch::global_asm`
+lint_builtin_global_macro_unsafety = using this macro is unsafe even though it does not need an `unsafe` block
+
 lint_builtin_impl_unsafe_method = implementation of an `unsafe` method
 
 lint_builtin_incomplete_features = the feature `{$name}` is incomplete and may not be safe to use and/or cause compiler crashes
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index faa35f51cd4..92f7497362e 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -393,6 +393,10 @@ impl EarlyLintPass for UnsafeCode {
                 }
             }
 
+            ast::ItemKind::GlobalAsm(..) => {
+                self.report_unsafe(cx, it.span, BuiltinUnsafe::GlobalAsm);
+            }
+
             _ => {}
         }
     }
@@ -1542,32 +1546,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds {
     }
 }
 
-declare_lint_pass!(
-    /// Lint constants that are erroneous.
-    /// Without this lint, we might not get any diagnostic if the constant is
-    /// unused within this crate, even though downstream crates can't use it
-    /// without producing an error.
-    UnusedBrokenConst => []
-);
-
-impl<'tcx> LateLintPass<'tcx> for UnusedBrokenConst {
-    fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
-        match it.kind {
-            hir::ItemKind::Const(_, _, body_id) => {
-                let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
-                // trigger the query once for all constants since that will already report the errors
-                // FIXME(generic_const_items): Does this work properly with generic const items?
-                cx.tcx.ensure().const_eval_poly(def_id);
-            }
-            hir::ItemKind::Static(_, _, body_id) => {
-                let def_id = cx.tcx.hir().body_owner_def_id(body_id).to_def_id();
-                cx.tcx.ensure().eval_static_initializer(def_id);
-            }
-            _ => {}
-        }
-    }
-}
-
 declare_lint! {
     /// The `trivial_bounds` lint detects trait bounds that don't depend on
     /// any type parameters.
diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs
index 5af2b6daec1..86434002e2c 100644
--- a/compiler/rustc_lint/src/context/diagnostics.rs
+++ b/compiler/rustc_lint/src/context/diagnostics.rs
@@ -1,3 +1,6 @@
+#![allow(rustc::diagnostic_outside_of_impl)]
+#![allow(rustc::untranslatable_diagnostic)]
+
 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
 use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder};
 use rustc_errors::{Applicability, SuggestionStyle};
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
index 21d4b6fa65b..bcff20fc260 100644
--- a/compiler/rustc_lint/src/errors.rs
+++ b/compiler/rustc_lint/src/errors.rs
@@ -1,5 +1,7 @@
 use crate::fluent_generated as fluent;
-use rustc_errors::{codes::*, AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp};
+use rustc_errors::{
+    codes::*, AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp,
+};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_session::lint::Level;
 use rustc_span::{Span, Symbol};
@@ -24,7 +26,11 @@ pub enum OverruledAttributeSub {
 }
 
 impl AddToDiagnostic for OverruledAttributeSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             OverruledAttributeSub::DefaultSource { id } => {
                 diag.note(fluent::lint_default_source);
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 40fb12b2107..c1a083bde8d 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1062,6 +1062,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
                 if self.lint_added_lints {
                     let lint = builtin::UNKNOWN_LINTS;
                     let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
+                    // FIXME: make this translatable
+                    #[allow(rustc::diagnostic_outside_of_impl)]
+                    #[allow(rustc::untranslatable_diagnostic)]
                     lint_level(
                         self.sess,
                         lint,
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index e50f4ca338b..d8e12c04f75 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -213,8 +213,6 @@ late_lint_methods!(
             ExplicitOutlivesRequirements: ExplicitOutlivesRequirements,
             InvalidValue: InvalidValue,
             DerefNullPtr: DerefNullPtr,
-            // May Depend on constants elsewhere
-            UnusedBrokenConst: UnusedBrokenConst,
             UnstableFeatures: UnstableFeatures,
             UngatedAsyncFnTrackCaller: UngatedAsyncFnTrackCaller,
             ArrayIntoIter: ArrayIntoIter::default(),
@@ -524,6 +522,11 @@ fn register_builtins(store: &mut LintStore) {
         "no longer needed, see RFC #3535 \
          <https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
     );
+    store.register_removed(
+        "suspicious_auto_trait_impls",
+        "no longer needed, see #93367 \
+         <https://github.com/rust-lang/rust/issues/93367> for more information",
+    );
 }
 
 fn register_internals(store: &mut LintStore) {
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index c204c67fc1f..f067c365170 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -5,8 +5,8 @@ use std::num::NonZero;
 use crate::errors::RequestedLevel;
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, DecorateLint, Diagnostic, DiagnosticBuilder,
-    DiagnosticMessage, DiagnosticStyledString, SubdiagnosticMessageOp, SuggestionStyle,
+    codes::*, AddToDiagnostic, Applicability, DecorateLint, DiagnosticBuilder, DiagnosticMessage,
+    DiagnosticStyledString, EmissionGuarantee, SubdiagnosticMessageOp, SuggestionStyle,
 };
 use rustc_hir::def_id::DefId;
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
@@ -114,6 +114,9 @@ pub enum BuiltinUnsafe {
     DeclUnsafeMethod,
     #[diag(lint_builtin_impl_unsafe_method)]
     ImplUnsafeMethod,
+    #[diag(lint_builtin_global_asm)]
+    #[note(lint_builtin_global_macro_unsafety)]
+    GlobalAsm,
 }
 
 #[derive(LintDiagnostic)]
@@ -267,17 +270,21 @@ pub struct SuggestChangingAssocTypes<'a, 'b> {
     pub ty: &'a rustc_hir::Ty<'b>,
 }
 
-impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+impl<'a, 'b> AddToDiagnostic for SuggestChangingAssocTypes<'a, 'b> {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
         // bound. Let's see if this type does that.
 
         // We use a HIR visitor to walk the type.
         use rustc_hir::intravisit::{self, Visitor};
-        struct WalkAssocTypes<'a> {
-            err: &'a mut Diagnostic,
+        struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> {
+            err: &'a mut DiagnosticBuilder<'b, G>,
         }
-        impl Visitor<'_> for WalkAssocTypes<'_> {
+        impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> {
             fn visit_qpath(
                 &mut self,
                 qpath: &rustc_hir::QPath<'_>,
@@ -320,7 +327,11 @@ pub struct BuiltinTypeAliasGenericBoundsSuggestion {
 }
 
 impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.multipart_suggestion(
             fluent::lint_suggestion,
             self.suggestions,
@@ -437,7 +448,11 @@ pub struct BuiltinUnpermittedTypeInitSub {
 }
 
 impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let mut err = self.err;
         loop {
             if let Some(span) = err.span {
@@ -488,7 +503,11 @@ pub struct BuiltinClashingExternSub<'a> {
 }
 
 impl AddToDiagnostic for BuiltinClashingExternSub<'_> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let mut expected_str = DiagnosticStyledString::new();
         expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
         let mut found_str = DiagnosticStyledString::new();
@@ -766,7 +785,11 @@ pub struct HiddenUnicodeCodepointsDiagLabels {
 }
 
 impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         for (c, span) in self.spans {
             diag.span_label(span, format!("{c:?}"));
         }
@@ -780,7 +803,11 @@ pub enum HiddenUnicodeCodepointsDiagSub {
 
 // Used because of multiple multipart_suggestion and note
 impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
                 diag.multipart_suggestion_with_style(
@@ -928,7 +955,11 @@ pub struct NonBindingLetSub {
 }
 
 impl AddToDiagnostic for NonBindingLetSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar;
 
         if can_suggest_binding {
@@ -1208,7 +1239,11 @@ pub enum NonSnakeCaseDiagSub {
 }
 
 impl AddToDiagnostic for NonSnakeCaseDiagSub {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             NonSnakeCaseDiagSub::Label { span } => {
                 diag.span_label(span, fluent::lint_label);
@@ -1401,7 +1436,11 @@ pub enum OverflowingBinHexSign {
 }
 
 impl AddToDiagnostic for OverflowingBinHexSign {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             OverflowingBinHexSign::Positive => {
                 diag.note(fluent::lint_positive_note);
@@ -1543,7 +1582,8 @@ pub enum AmbiguousWidePointerComparisons<'a> {
 #[multipart_suggestion(
     lint_addr_metadata_suggestion,
     style = "verbose",
-    applicability = "machine-applicable"
+    // FIXME(#53934): make machine-applicable again
+    applicability = "maybe-incorrect"
 )]
 pub struct AmbiguousWidePointerComparisonsAddrMetadataSuggestion<'a> {
     pub ne: &'a str,
@@ -1562,7 +1602,8 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
     #[multipart_suggestion(
         lint_addr_suggestion,
         style = "verbose",
-        applicability = "machine-applicable"
+        // FIXME(#53934): make machine-applicable again
+        applicability = "maybe-incorrect"
     )]
     AddrEq {
         ne: &'a str,
@@ -1578,7 +1619,8 @@ pub enum AmbiguousWidePointerComparisonsAddrSuggestion<'a> {
     #[multipart_suggestion(
         lint_addr_suggestion,
         style = "verbose",
-        applicability = "machine-applicable"
+        // FIXME(#53934): make machine-applicable again
+        applicability = "maybe-incorrect"
     )]
     Cast {
         deref_left: &'a str,
diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs
index 3f5d3c25971..84a050a242a 100644
--- a/compiler/rustc_lint_defs/src/builtin.rs
+++ b/compiler/rustc_lint_defs/src/builtin.rs
@@ -90,7 +90,6 @@ declare_lint_pass! {
         SOFT_UNSTABLE,
         STABLE_FEATURES,
         STATIC_MUT_REFS,
-        SUSPICIOUS_AUTO_TRAIT_IMPLS,
         TEST_UNSTABLE_LINT,
         TEXT_DIRECTION_CODEPOINT_IN_COMMENT,
         TRIVIAL_CASTS,
@@ -1503,7 +1502,7 @@ declare_lint! {
     Warn,
     "distinct impls distinguished only by the leak-check code",
     @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
+        reason: FutureIncompatibilityReason::Custom("the behavior may change in a future release"),
         reference: "issue #56105 <https://github.com/rust-lang/rust/issues/56105>",
     };
 }
@@ -4033,40 +4032,6 @@ declare_lint! {
 }
 
 declare_lint! {
-    /// The `suspicious_auto_trait_impls` lint checks for potentially incorrect
-    /// implementations of auto traits.
-    ///
-    /// ### Example
-    ///
-    /// ```rust
-    /// struct Foo<T>(T);
-    ///
-    /// unsafe impl<T> Send for Foo<*const T> {}
-    /// ```
-    ///
-    /// {{produces}}
-    ///
-    /// ### Explanation
-    ///
-    /// A type can implement auto traits, e.g. `Send`, `Sync` and `Unpin`,
-    /// in two different ways: either by writing an explicit impl or if
-    /// all fields of the type implement that auto trait.
-    ///
-    /// The compiler disables the automatic implementation if an explicit one
-    /// exists for given type constructor. The exact rules governing this
-    /// were previously unsound, quite subtle, and have been recently modified.
-    /// This change caused the automatic implementation to be disabled in more
-    /// cases, potentially breaking some code.
-    pub SUSPICIOUS_AUTO_TRAIT_IMPLS,
-    Warn,
-    "the rules governing auto traits have recently changed resulting in potential breakage",
-    @future_incompatible = FutureIncompatibleInfo {
-        reason: FutureIncompatibilityReason::FutureReleaseSemanticsChange,
-        reference: "issue #93367 <https://github.com/rust-lang/rust/issues/93367>",
-    };
-}
-
-declare_lint! {
     /// The `deprecated_where_clause_location` lint detects when a where clause in front of the equals
     /// in an associated type.
     ///
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 09b1f03f151..d4e5c78c492 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -227,8 +227,8 @@ impl Level {
     }
 
     /// Converts a lower-case string to a level. This will never construct the expect
-    /// level as that would require a [`LintExpectationId`]
-    pub fn from_str(x: &str) -> Option<Level> {
+    /// level as that would require a [`LintExpectationId`].
+    pub fn from_str(x: &str) -> Option<Self> {
         match x {
             "allow" => Some(Level::Allow),
             "warn" => Some(Level::Warn),
@@ -238,17 +238,21 @@ impl Level {
         }
     }
 
-    /// Converts a symbol to a level.
-    pub fn from_attr(attr: &Attribute) -> Option<Level> {
-        match attr.name_or_empty() {
-            sym::allow => Some(Level::Allow),
-            sym::expect => Some(Level::Expect(LintExpectationId::Unstable {
-                attr_id: attr.id,
-                lint_index: None,
-            })),
-            sym::warn => Some(Level::Warn),
-            sym::deny => Some(Level::Deny),
-            sym::forbid => Some(Level::Forbid),
+    /// Converts an `Attribute` to a level.
+    pub fn from_attr(attr: &Attribute) -> Option<Self> {
+        Self::from_symbol(attr.name_or_empty(), Some(attr.id))
+    }
+
+    /// Converts a `Symbol` to a level.
+    pub fn from_symbol(s: Symbol, id: Option<AttrId>) -> Option<Self> {
+        match (s, id) {
+            (sym::allow, _) => Some(Level::Allow),
+            (sym::expect, Some(attr_id)) => {
+                Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None }))
+            }
+            (sym::warn, _) => Some(Level::Warn),
+            (sym::deny, _) => Some(Level::Deny),
+            (sym::forbid, _) => Some(Level::Forbid),
             _ => None,
         }
     }
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index b45706fd1e5..7326f2e8e2a 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -418,7 +418,11 @@ extern "C" LLVMAttributeRef LLVMRustCreateMemoryEffectsAttr(LLVMContextRef C,
   }
 }
 
-// Enable a fast-math flag
+// Enable all fast-math flags, including those which will cause floating-point operations
+// to return poison for some well-defined inputs. This function can only be used to build
+// unsafe Rust intrinsics. That unsafety does permit additional optimizations, but at the
+// time of writing, their value is not well-understood relative to those enabled by
+// LLVMRustSetAlgebraicMath.
 //
 // https://llvm.org/docs/LangRef.html#fast-math-flags
 extern "C" void LLVMRustSetFastMath(LLVMValueRef V) {
@@ -427,6 +431,25 @@ extern "C" void LLVMRustSetFastMath(LLVMValueRef V) {
   }
 }
 
+// Enable fast-math flags which permit algebraic transformations that are not allowed by
+// IEEE floating point. For example:
+// a + (b + c) = (a + b) + c
+// and
+// a / b = a * (1 / b)
+// Note that this does NOT enable any flags which can cause a floating-point operation on
+// well-defined inputs to return poison, and therefore this function can be used to build
+// safe Rust intrinsics (such as fadd_algebraic).
+//
+// https://llvm.org/docs/LangRef.html#fast-math-flags
+extern "C" void LLVMRustSetAlgebraicMath(LLVMValueRef V) {
+  if (auto I = dyn_cast<Instruction>(unwrap<Value>(V))) {
+    I->setHasAllowReassoc(true);
+    I->setHasAllowContract(true);
+    I->setHasAllowReciprocal(true);
+    I->setHasNoSignedZeros(true);
+  }
+}
+
 extern "C" LLVMValueRef
 LLVMRustBuildAtomicLoad(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Source,
                         const char *Name, LLVMAtomicOrdering Order) {
diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
index 3a5f289559e..323614c222f 100644
--- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
+++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs
@@ -87,9 +87,13 @@ impl SubdiagnosticDeriveBuilder {
         let f = &self.f;
         let ret = structure.gen_impl(quote! {
             gen impl rustc_errors::AddToDiagnostic for @Self {
-                fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F)
-                where
-                    __F: rustc_errors::SubdiagnosticMessageOp,
+                fn add_to_diagnostic_with<__G, __F>(
+                    self,
+                    #diag: &mut rustc_errors::DiagnosticBuilder<'_, __G>,
+                    #f: __F
+                ) where
+                    __G: rustc_errors::EmissionGuarantee,
+                    __F: rustc_errors::SubdiagnosticMessageOp<__G>,
                 {
                     #implementation
                 }
diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs
index 619f93c8a53..14e6a06839e 100644
--- a/compiler/rustc_macros/src/lib.rs
+++ b/compiler/rustc_macros/src/lib.rs
@@ -41,6 +41,20 @@ pub fn symbols(input: TokenStream) -> TokenStream {
     symbols::symbols(input.into()).into()
 }
 
+/// Derive an extension trait for a given impl block. The trait name
+/// goes into the parenthesized args of the macro, for greppability.
+/// For example:
+/// ```
+/// use rustc_macros::extension;
+/// #[extension(pub trait Foo)]
+/// impl i32 { fn hello() {} }
+/// ```
+///
+/// expands to:
+/// ```
+/// pub trait Foo { fn hello(); }
+/// impl Foo for i32 { fn hello() {} }
+/// ```
 #[proc_macro_attribute]
 pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream {
     extension::extension(attr, input)
diff --git a/compiler/rustc_metadata/messages.ftl b/compiler/rustc_metadata/messages.ftl
index 8da6f0007f0..a1c6fba4d43 100644
--- a/compiler/rustc_metadata/messages.ftl
+++ b/compiler/rustc_metadata/messages.ftl
@@ -45,7 +45,7 @@ metadata_crate_not_panic_runtime =
     the crate `{$crate_name}` is not a panic runtime
 
 metadata_dl_error =
-    {$err}
+    {$path}{$err}
 
 metadata_empty_link_name =
     link name must not be empty
diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs
index c18f0e7b7b9..f65fe1a29c7 100644
--- a/compiler/rustc_metadata/src/creader.rs
+++ b/compiler/rustc_metadata/src/creader.rs
@@ -692,20 +692,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         path: &Path,
         stable_crate_id: StableCrateId,
     ) -> Result<&'static [ProcMacro], CrateError> {
-        // Make sure the path contains a / or the linker will search for it.
-        let path = try_canonicalize(path).unwrap();
-        let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
-
         let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
-        let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
-            .map_err(|err| CrateError::DlSym(err.to_string()))?;
-
-        // Intentionally leak the dynamic library. We can't ever unload it
-        // since the library can make things that will live arbitrarily long.
-        let sym = unsafe { sym.into_raw() };
-        std::mem::forget(lib);
-
-        Ok(unsafe { **sym })
+        Ok(unsafe { *load_symbol_from_dylib::<*const &[ProcMacro]>(path, &sym_name)? })
     }
 
     fn inject_panic_runtime(&mut self, krate: &ast::Crate) {
@@ -926,7 +914,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
         what: &str,
         needs_dep: &dyn Fn(&CrateMetadata) -> bool,
     ) {
-        // don't perform this validation if the session has errors, as one of
+        // Don't perform this validation if the session has errors, as one of
         // those errors may indicate a circular dependency which could cause
         // this to stack overflow.
         if self.dcx().has_errors().is_some() {
@@ -1070,16 +1058,6 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
     pub fn maybe_process_path_extern(&mut self, name: Symbol) -> Option<CrateNum> {
         self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok()
     }
-
-    pub fn unload_unused_crates(&mut self) {
-        for opt_cdata in &mut self.cstore.metas {
-            if let Some(cdata) = opt_cdata
-                && !cdata.used()
-            {
-                *opt_cdata = None;
-            }
-        }
-    }
 }
 
 fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
@@ -1126,6 +1104,10 @@ fn alloc_error_handler_spans(krate: &ast::Crate) -> Vec<Span> {
     f.spans
 }
 
+fn format_dlopen_err(e: &(dyn std::error::Error + 'static)) -> String {
+    e.sources().map(|e| format!(": {e}")).collect()
+}
+
 // On Windows the compiler would sometimes intermittently fail to open the
 // proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
 // system still holds a lock on the file, so we retry a few times before calling it
@@ -1164,9 +1146,43 @@ fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, S
 
     let last_error = last_error.unwrap();
     let message = if let Some(src) = last_error.source() {
-        format!("{last_error} ({src}) (retried {max_attempts} times)")
+        format!("{} ({src}) (retried {max_attempts} times)", format_dlopen_err(&last_error))
     } else {
-        format!("{last_error} (retried {max_attempts} times)")
+        format!("{} (retried {max_attempts} times)", format_dlopen_err(&last_error))
     };
     Err(message)
 }
+
+pub enum DylibError {
+    DlOpen(String, String),
+    DlSym(String, String),
+}
+
+impl From<DylibError> for CrateError {
+    fn from(err: DylibError) -> CrateError {
+        match err {
+            DylibError::DlOpen(path, err) => CrateError::DlOpen(path, err),
+            DylibError::DlSym(path, err) => CrateError::DlSym(path, err),
+        }
+    }
+}
+
+pub unsafe fn load_symbol_from_dylib<T: Copy>(
+    path: &Path,
+    sym_name: &str,
+) -> Result<T, DylibError> {
+    // Make sure the path contains a / or the linker will search for it.
+    let path = try_canonicalize(path).unwrap();
+    let lib =
+        load_dylib(&path, 5).map_err(|err| DylibError::DlOpen(path.display().to_string(), err))?;
+
+    let sym = unsafe { lib.get::<T>(sym_name.as_bytes()) }
+        .map_err(|err| DylibError::DlSym(path.display().to_string(), format_dlopen_err(&err)))?;
+
+    // Intentionally leak the dynamic library. We can't ever unload it
+    // since the library can make things that will live arbitrarily long.
+    let sym = unsafe { sym.into_raw() };
+    std::mem::forget(lib);
+
+    Ok(*sym)
+}
diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs
index d17bf0cf708..9a05d9ac0de 100644
--- a/compiler/rustc_metadata/src/errors.rs
+++ b/compiler/rustc_metadata/src/errors.rs
@@ -505,6 +505,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for MultipleCandidates {
         diag.code(E0464);
         diag.span(self.span);
         for (i, candidate) in self.candidates.iter().enumerate() {
+            // FIXME: make this translatable
+            #[allow(rustc::untranslatable_diagnostic)]
             diag.note(format!("candidate #{}: {}", i + 1, candidate.display()));
         }
         diag
@@ -533,6 +535,7 @@ pub struct StableCrateIdCollision {
 pub struct DlError {
     #[primary_span]
     pub span: Span,
+    pub path: String,
     pub err: String,
 }
 
@@ -601,6 +604,8 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for InvalidMetadataFiles {
         diag.code(E0786);
         diag.span(self.span);
         for crate_rejection in self.crate_rejections {
+            // FIXME: make this translatable
+            #[allow(rustc::untranslatable_diagnostic)]
             diag.note(crate_rejection);
         }
         diag
diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs
index 70ad8598957..f133a2f5f73 100644
--- a/compiler/rustc_metadata/src/lib.rs
+++ b/compiler/rustc_metadata/src/lib.rs
@@ -3,6 +3,7 @@
 #![feature(rustdoc_internals)]
 #![allow(internal_features)]
 #![feature(decl_macro)]
+#![feature(error_iter)]
 #![feature(extract_if)]
 #![feature(coroutines)]
 #![feature(generic_nonzero)]
@@ -39,6 +40,7 @@ pub mod errors;
 pub mod fs;
 pub mod locator;
 
+pub use creader::{load_symbol_from_dylib, DylibError};
 pub use fs::{emit_wrapper_file, METADATA_FILENAME};
 pub use native_libs::find_native_static_library;
 pub use rmeta::{encode_metadata, rendered_const, EncodedMetadata, METADATA_HEADER};
diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs
index 1ab965e2876..15f56db6278 100644
--- a/compiler/rustc_metadata/src/locator.rs
+++ b/compiler/rustc_metadata/src/locator.rs
@@ -921,8 +921,8 @@ pub(crate) enum CrateError {
     MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
     SymbolConflictsCurrent(Symbol),
     StableCrateIdCollision(Symbol, Symbol),
-    DlOpen(String),
-    DlSym(String),
+    DlOpen(String, String),
+    DlSym(String, String),
     LocatorCombined(Box<CombinedLocatorError>),
     NotFound(Symbol),
 }
@@ -967,8 +967,8 @@ impl CrateError {
             CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
                 dcx.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
             }
-            CrateError::DlOpen(s) | CrateError::DlSym(s) => {
-                dcx.emit_err(errors::DlError { span, err: s });
+            CrateError::DlOpen(path, err) | CrateError::DlSym(path, err) => {
+                dcx.emit_err(errors::DlError { span, path, err });
             }
             CrateError::LocatorCombined(locator) => {
                 let crate_name = locator.crate_name;
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 9df28799f4f..2f507b258fe 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -519,6 +519,16 @@ pub(in crate::rmeta) fn provide(providers: &mut Providers) {
             tcx.untracked().cstore.freeze();
             tcx.arena.alloc_from_iter(CStore::from_tcx(tcx).iter_crate_data().map(|(cnum, _)| cnum))
         },
+        used_crates: |tcx, ()| {
+            // The list of loaded crates is now frozen in query cache,
+            // so make sure cstore is not mutably accessed from here on.
+            tcx.untracked().cstore.freeze();
+            tcx.arena.alloc_from_iter(
+                CStore::from_tcx(tcx)
+                    .iter_crate_data()
+                    .filter_map(|(cnum, data)| data.used().then_some(cnum)),
+            )
+        },
         ..providers.queries
     };
     provide_extern(&mut providers.extern_queries);
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index c648b2bfe9b..a532635669d 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -47,7 +47,7 @@ macro_rules! arena_types {
                         rustc_middle::traits::query::DropckOutlivesResult<'tcx>
                     >
                 >,
-            [] normalize_projection_ty:
+            [] normalize_canonicalized_projection_ty:
                 rustc_middle::infer::canonical::Canonical<'tcx,
                     rustc_middle::infer::canonical::QueryResponse<'tcx,
                         rustc_middle::traits::query::NormalizationResult<'tcx>
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 2a6f473cd32..1e9e9947db5 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -2,7 +2,7 @@ use std::cmp;
 
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::sorted_map::SortedMap;
-use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan};
+use rustc_errors::{DiagnosticBuilder, DiagnosticMessage, MultiSpan};
 use rustc_hir::{HirId, ItemLocalId};
 use rustc_session::lint::{
     builtin::{self, FORBIDDEN_LINT_GROUPS},
@@ -204,7 +204,7 @@ pub fn explain_lint_level_source(
     lint: &'static Lint,
     level: Level,
     src: LintLevelSource,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_, ()>,
 ) {
     let name = lint.name_lower();
     if let Level::Allow = level {
@@ -359,7 +359,7 @@ pub fn lint_level(
         // Lint diagnostics that are covered by the expect level will not be emitted outside
         // the compiler. It is therefore not necessary to add any information for the user.
         // This will therefore directly call the decorate function which will in turn emit
-        // the `Diagnostic`.
+        // the diagnostic.
         if let Level::Expect(_) = level {
             decorate(&mut err);
             err.emit();
@@ -401,7 +401,7 @@ pub fn lint_level(
 
         // Finally, run `decorate`.
         decorate(&mut err);
-        explain_lint_level_source(lint, level, src, &mut *err);
+        explain_lint_level_source(lint, level, src, &mut err);
         err.emit()
     }
     lint_level_impl(sess, lint, level, src, span, msg, Box::new(decorate))
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 2f624ab0527..5f3ecf34416 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -9,7 +9,7 @@ use rustc_attr::{
     self as attr, ConstStability, DefaultBodyStability, DeprecatedSince, Deprecation, Stability,
 };
 use rustc_data_structures::unord::UnordMap;
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_feature::GateIssue;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
@@ -125,7 +125,7 @@ pub fn report_unstable(
 }
 
 pub fn deprecation_suggestion(
-    diag: &mut Diagnostic,
+    diag: &mut DiagnosticBuilder<'_, ()>,
     kind: &str,
     suggestion: Option<Symbol>,
     span: Span,
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 5be45c33e11..e87bc581e6e 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -31,7 +31,7 @@ use crate::query::plumbing::{
 };
 use crate::thir;
 use crate::traits::query::{
-    CanonicalPredicateGoal, CanonicalProjectionGoal, CanonicalTyGoal,
+    CanonicalAliasGoal, CanonicalPredicateGoal, CanonicalTyGoal,
     CanonicalTypeOpAscribeUserTypeGoal, CanonicalTypeOpEqGoal, CanonicalTypeOpNormalizeGoal,
     CanonicalTypeOpProvePredicateGoal, CanonicalTypeOpSubtypeGoal, NoSolution,
 };
@@ -1872,6 +1872,13 @@ rustc_queries! {
         eval_always
         desc { "fetching all foreign CrateNum instances" }
     }
+    // Crates that are loaded non-speculatively (not for diagnostics or doc links).
+    // FIXME: This is currently only used for collecting lang items, but should be used instead of
+    // `crates` in most other cases too.
+    query used_crates(_: ()) -> &'tcx [CrateNum] {
+        eval_always
+        desc { "fetching `CrateNum`s for all crates loaded non-speculatively" }
+    }
 
     /// A list of all traits in a crate, used by rustdoc and error reporting.
     query traits(_: CrateNum) -> &'tcx [DefId] {
@@ -1931,9 +1938,13 @@ rustc_queries! {
         arena_cache
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_projection_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_projection_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
@@ -1941,9 +1952,13 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value.value }
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_weak_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_weak_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
@@ -1951,9 +1966,13 @@ rustc_queries! {
         desc { "normalizing `{}`", goal.value.value }
     }
 
-    /// Do not call this query directly: invoke `normalize` instead.
-    query normalize_inherent_projection_ty(
-        goal: CanonicalProjectionGoal<'tcx>
+    /// <div class="warning">
+    ///
+    /// Do not call this query directly: Invoke `normalize` instead.
+    ///
+    /// </div>
+    query normalize_canonicalized_inherent_projection_ty(
+        goal: CanonicalAliasGoal<'tcx>
     ) -> Result<
         &'tcx Canonical<'tcx, canonical::QueryResponse<'tcx, NormalizationResult<'tcx>>>,
         NoSolution,
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 119e0a49acf..b3c09d1d152 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -16,7 +16,7 @@ use crate::ty::GenericArgsRef;
 use crate::ty::{self, AdtKind, Ty};
 
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, Diagnostic};
+use rustc_errors::{Applicability, DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_span::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -908,7 +908,7 @@ pub enum ObjectSafetyViolationSolution {
 }
 
 impl ObjectSafetyViolationSolution {
-    pub fn add_to(self, err: &mut Diagnostic) {
+    pub fn add_to<G: EmissionGuarantee>(self, err: &mut DiagnosticBuilder<'_, G>) {
         match self {
             ObjectSafetyViolationSolution::None => {}
             ObjectSafetyViolationSolution::AddSelfOrMakeSized {
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index 61d96cad57d..12e4f70ba57 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -67,7 +67,7 @@ pub mod type_op {
     }
 }
 
-pub type CanonicalProjectionGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
+pub type CanonicalAliasGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, ty::AliasTy<'tcx>>>;
 
 pub type CanonicalTyGoal<'tcx> = Canonical<'tcx, ty::ParamEnvAnd<'tcx, Ty<'tcx>>>;
 
@@ -177,10 +177,10 @@ pub struct MethodAutoderefBadTy<'tcx> {
     pub ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
 }
 
-/// Result from the `normalize_projection_ty` query.
+/// Result of the `normalize_canonicalized_{{,inherent_}projection,weak}_ty` queries.
 #[derive(Clone, Debug, HashStable, TypeFoldable, TypeVisitable)]
 pub struct NormalizationResult<'tcx> {
-    /// Result of normalization.
+    /// Result of the normalization.
     pub normalized_ty: Ty<'tcx>,
 }
 
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index cc734e7157f..9d59f779470 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -153,11 +153,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     ) -> Self::Const {
         Const::new_bound(self, debruijn, var, ty)
     }
-
-    fn expect_error_or_delayed_bug() {
-        let has_errors = ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs());
-        assert!(has_errors.is_some());
-    }
 }
 
 type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 7cb326ce696..f379cf27a5f 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -11,7 +11,7 @@ use crate::ty::{
 };
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
+use rustc_errors::{Applicability, DiagnosticArgValue, DiagnosticBuilder, IntoDiagnosticArg};
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
@@ -111,7 +111,7 @@ where
 pub fn suggest_arbitrary_trait_bound<'tcx>(
     tcx: TyCtxt<'tcx>,
     generics: &hir::Generics<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     trait_pred: PolyTraitPredicate<'tcx>,
     associated_ty: Option<(&'static str, Ty<'tcx>)>,
 ) -> bool {
@@ -216,7 +216,7 @@ fn suggest_changing_unsized_bound(
 pub fn suggest_constraining_type_param(
     tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     param_name: &str,
     constraint: &str,
     def_id: Option<DefId>,
@@ -235,7 +235,7 @@ pub fn suggest_constraining_type_param(
 pub fn suggest_constraining_type_params<'a>(
     tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
     span_to_replace: Option<Span>,
 ) -> bool {
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 80b763d1469..e15f0378846 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -351,7 +351,7 @@ impl<'tcx> TyCtxt<'tcx> {
         })
         .expect("could not write to `String`");
 
-        if !self.sess.opts.unstable_opts.write_long_types_to_disk {
+        if !self.sess.opts.unstable_opts.write_long_types_to_disk || self.sess.opts.verbose {
             return regular;
         }
 
diff --git a/compiler/rustc_middle/src/ty/flags.rs b/compiler/rustc_middle/src/ty/flags.rs
index 0f4b5fe228c..18cf5445e56 100644
--- a/compiler/rustc_middle/src/ty/flags.rs
+++ b/compiler/rustc_middle/src/ty/flags.rs
@@ -181,9 +181,10 @@ impl FlagComputation {
 
             &ty::Alias(kind, data) => {
                 self.add_flags(match kind {
-                    ty::Weak | ty::Projection => TypeFlags::HAS_TY_PROJECTION,
-                    ty::Inherent => TypeFlags::HAS_TY_INHERENT,
+                    ty::Projection => TypeFlags::HAS_TY_PROJECTION,
+                    ty::Weak => TypeFlags::HAS_TY_WEAK,
                     ty::Opaque => TypeFlags::HAS_TY_OPAQUE,
+                    ty::Inherent => TypeFlags::HAS_TY_INHERENT,
                 });
 
                 self.add_alias_ty(data);
diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs
index a03ee78a29a..4287b382604 100644
--- a/compiler/rustc_middle/src/ty/typeck_results.rs
+++ b/compiler/rustc_middle/src/ty/typeck_results.rs
@@ -410,8 +410,7 @@ impl<'tcx> TypeckResults<'tcx> {
 
     pub fn extract_binding_mode(&self, s: &Session, id: HirId, sp: Span) -> Option<BindingMode> {
         self.pat_binding_modes().get(id).copied().or_else(|| {
-            s.dcx().span_delayed_bug(sp, "missing binding mode");
-            None
+            s.dcx().span_bug(sp, "missing binding mode");
         })
     }
 
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index 3f539945841..72a1905c147 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -11,6 +11,7 @@ use crate::ty::{GenericArgKind, GenericArgsRef};
 use rustc_apfloat::Float as _;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher};
+use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -867,6 +868,63 @@ impl<'tcx> TyCtxt<'tcx> {
 
         self.mk_args_from_iter(args.into_iter().map(|arg| arg.into()).chain(opt_const_param))
     }
+
+    /// Expand any [weak alias types][weak] contained within the given `value`.
+    ///
+    /// This should be used over other normalization routines in situations where
+    /// it's important not to normalize other alias types and where the predicates
+    /// on the corresponding type alias shouldn't be taken into consideration.
+    ///
+    /// Whenever possible **prefer not to use this function**! Instead, use standard
+    /// normalization routines or if feasible don't normalize at all.
+    ///
+    /// This function comes in handy if you want to mimic the behavior of eager
+    /// type alias expansion in a localized manner.
+    ///
+    /// <div class="warning">
+    /// This delays a bug on overflow! Therefore you need to be certain that the
+    /// contained types get fully normalized at a later stage. Note that even on
+    /// overflow all well-behaved weak alias types get expanded correctly, so the
+    /// result is still useful.
+    /// </div>
+    ///
+    /// [weak]: ty::Weak
+    pub fn expand_weak_alias_tys<T: TypeFoldable<TyCtxt<'tcx>>>(self, value: T) -> T {
+        value.fold_with(&mut WeakAliasTypeExpander { tcx: self, depth: 0 })
+    }
+
+    /// Peel off all [weak alias types] in this type until there are none left.
+    ///
+    /// This only expands weak alias types in “head” / outermost positions. It can
+    /// be used over [expand_weak_alias_tys] as an optimization in situations where
+    /// one only really cares about the *kind* of the final aliased type but not
+    /// the types the other constituent types alias.
+    ///
+    /// <div class="warning">
+    /// This delays a bug on overflow! Therefore you need to be certain that the
+    /// type gets fully normalized at a later stage.
+    /// </div>
+    ///
+    /// [weak]: ty::Weak
+    /// [expand_weak_alias_tys]: Self::expand_weak_alias_tys
+    pub fn peel_off_weak_alias_tys(self, mut ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty::Alias(ty::Weak, _) = ty.kind() else { return ty };
+
+        let limit = self.recursion_limit();
+        let mut depth = 0;
+
+        while let ty::Alias(ty::Weak, alias) = ty.kind() {
+            if !limit.value_within_limit(depth) {
+                let guar = self.dcx().delayed_bug("overflow expanding weak alias type");
+                return Ty::new_error(self, guar);
+            }
+
+            ty = self.type_of(alias.def_id).instantiate(self, alias.args);
+            depth += 1;
+        }
+
+        ty
+    }
 }
 
 struct OpaqueTypeExpander<'tcx> {
@@ -1002,6 +1060,42 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for OpaqueTypeExpander<'tcx> {
     }
 }
 
+struct WeakAliasTypeExpander<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    depth: usize,
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for WeakAliasTypeExpander<'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.tcx
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !ty.has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
+            return ty;
+        }
+        let ty::Alias(ty::Weak, alias) = ty.kind() else {
+            return ty.super_fold_with(self);
+        };
+        if !self.tcx.recursion_limit().value_within_limit(self.depth) {
+            let guar = self.tcx.dcx().delayed_bug("overflow expanding weak alias type");
+            return Ty::new_error(self.tcx, guar);
+        }
+
+        self.depth += 1;
+        ensure_sufficient_stack(|| {
+            self.tcx.type_of(alias.def_id).instantiate(self.tcx, alias.args).fold_with(self)
+        })
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        if !ct.ty().has_type_flags(ty::TypeFlags::HAS_TY_WEAK) {
+            return ct;
+        }
+        ct.super_fold_with(self)
+    }
+}
+
 impl<'tcx> Ty<'tcx> {
     /// Returns the `Size` for primitive types (bool, uint, int, char, float).
     pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size {
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 59292a281ed..59d09c3dc78 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -2,6 +2,7 @@ use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sso::SsoHashSet;
+use rustc_type_ir::fold::TypeFoldable;
 use std::ops::ControlFlow;
 
 pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
@@ -109,10 +110,10 @@ impl<'tcx> TyCtxt<'tcx> {
     /// variables will also be equated.
     pub fn collect_constrained_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
+        value: Binder<'tcx, T>,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
         self.collect_late_bound_regions(value, true)
     }
@@ -120,24 +121,26 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Returns a set of all late-bound regions that appear in `value` anywhere.
     pub fn collect_referenced_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
+        value: Binder<'tcx, T>,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
         self.collect_late_bound_regions(value, false)
     }
 
     fn collect_late_bound_regions<T>(
         self,
-        value: &Binder<'tcx, T>,
-        just_constraint: bool,
+        value: Binder<'tcx, T>,
+        just_constrained: bool,
     ) -> FxHashSet<ty::BoundRegionKind>
     where
-        T: TypeVisitable<TyCtxt<'tcx>>,
+        T: TypeFoldable<TyCtxt<'tcx>>,
     {
-        let mut collector = LateBoundRegionsCollector::new(just_constraint);
-        let result = value.as_ref().skip_binder().visit_with(&mut collector);
+        let mut collector = LateBoundRegionsCollector::new(just_constrained);
+        let value = value.skip_binder();
+        let value = if just_constrained { self.expand_weak_alias_tys(value) } else { value };
+        let result = value.visit_with(&mut collector);
         assert!(result.is_continue()); // should never have stopped early
         collector.regions
     }
@@ -258,11 +261,7 @@ struct LateBoundRegionsCollector {
 
 impl LateBoundRegionsCollector {
     fn new(just_constrained: bool) -> Self {
-        LateBoundRegionsCollector {
-            current_index: ty::INNERMOST,
-            regions: Default::default(),
-            just_constrained,
-        }
+        Self { current_index: ty::INNERMOST, regions: Default::default(), just_constrained }
     }
 }
 
@@ -278,12 +277,16 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for LateBoundRegionsCollector {
     }
 
     fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // if we are only looking for "constrained" region, we have to
-        // ignore the inputs to a projection, as they may not appear
-        // in the normalized form
         if self.just_constrained {
-            if let ty::Alias(..) = t.kind() {
-                return ControlFlow::Continue(());
+            match t.kind() {
+                // If we are only looking for "constrained" regions, we have to ignore the
+                // inputs to a projection as they may not appear in the normalized form.
+                ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, _) => {
+                    return ControlFlow::Continue(());
+                }
+                // All weak alias types should've been expanded beforehand.
+                ty::Alias(ty::Weak, _) => bug!("unexpected weak alias type"),
+                _ => {}
             }
         }
 
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index 7c3d2671d59..a557f61b016 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -110,15 +110,12 @@ fn lit_to_mir_constant<'tcx>(
     let LitToConstInput { lit, ty, neg } = lit_input;
     let trunc = |n| {
         let param_ty = ty::ParamEnv::reveal_all().and(ty);
-        let width =
-            tcx.layout_of(param_ty)
-                .map_err(|_| {
-                    LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
-                        "couldn't compute width of literal: {:?}",
-                        lit_input.lit
-                    )))
-                })?
-                .size;
+        let width = match tcx.layout_of(param_ty) {
+            Ok(layout) => layout.size,
+            Err(_) => {
+                tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit))
+            }
+        };
         trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
         let result = width.truncate(n);
         trace!("trunc result: {}", result);
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 35f5a6bfac5..88a5eae281b 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -22,8 +22,6 @@ use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
 use rustc_span::symbol::Symbol;
 use rustc_span::{BytePos, Pos, Span};
 use rustc_target::abi::VariantIdx;
-use smallvec::{smallvec, SmallVec};
-
 // helper functions, broken out by category:
 mod simplify;
 mod test;
@@ -949,12 +947,16 @@ struct Candidate<'pat, 'tcx> {
     has_guard: bool,
 
     /// All of these must be satisfied...
-    match_pairs: SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+    // Invariant: all the `MatchPair`s are recursively simplified.
+    // Invariant: or-patterns must be sorted at the end.
+    match_pairs: Vec<MatchPair<'pat, 'tcx>>,
 
     /// ...these bindings established...
+    // Invariant: not mutated outside `Candidate::new()`.
     bindings: Vec<Binding<'tcx>>,
 
     /// ...and these types asserted...
+    // Invariant: not mutated outside `Candidate::new()`.
     ascriptions: Vec<Ascription<'tcx>>,
 
     /// ...and if this is non-empty, one of these subcandidates also has to match...
@@ -974,19 +976,27 @@ impl<'tcx, 'pat> Candidate<'pat, 'tcx> {
         place: PlaceBuilder<'tcx>,
         pattern: &'pat Pat<'tcx>,
         has_guard: bool,
-        cx: &Builder<'_, 'tcx>,
+        cx: &mut Builder<'_, 'tcx>,
     ) -> Self {
-        Candidate {
+        let mut candidate = Candidate {
             span: pattern.span,
             has_guard,
-            match_pairs: smallvec![MatchPair::new(place, pattern, cx)],
+            match_pairs: vec![MatchPair::new(place, pattern, cx)],
             bindings: Vec::new(),
             ascriptions: Vec::new(),
             subcandidates: Vec::new(),
             otherwise_block: None,
             pre_binding_block: None,
             next_candidate_pre_binding_block: None,
-        }
+        };
+
+        cx.simplify_match_pairs(
+            &mut candidate.match_pairs,
+            &mut candidate.bindings,
+            &mut candidate.ascriptions,
+        );
+
+        candidate
     }
 
     /// Visit the leaf candidates (those with no subcandidates) contained in
@@ -1042,13 +1052,18 @@ struct Ascription<'tcx> {
     variance: ty::Variance,
 }
 
-#[derive(Clone, Debug)]
+#[derive(Debug, Clone)]
 pub(crate) struct MatchPair<'pat, 'tcx> {
-    // this place...
+    // This place...
     place: PlaceBuilder<'tcx>,
 
     // ... must match this pattern.
+    // Invariant: after creation and simplification in `Candidate::new()`, all match pairs must be
+    // simplified, i.e. require a test.
     pattern: &'pat Pat<'tcx>,
+
+    /// Precomputed sub-match pairs of `pattern`.
+    subpairs: Vec<Self>,
 }
 
 /// See [`Test`] for more.
@@ -1165,12 +1180,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         candidates: &mut [&mut Candidate<'pat, 'tcx>],
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
-        // Start by simplifying candidates. Once this process is complete, all
-        // the match pairs which remain require some form of test, whether it
-        // be a switch or pattern comparison.
         let mut split_or_candidate = false;
         for candidate in &mut *candidates {
-            split_or_candidate |= self.simplify_candidate(candidate);
+            if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
+                &*candidate.match_pairs
+            {
+                // Split a candidate in which the only match-pair is an or-pattern into multiple
+                // candidates. This is so that
+                //
+                // match x {
+                //     0 | 1 => { ... },
+                //     2 | 3 => { ... },
+                // }
+                //
+                // only generates a single switch.
+                candidate.subcandidates =
+                    self.create_or_subcandidates(place, pats, candidate.has_guard);
+                candidate.match_pairs.pop();
+                split_or_candidate = true;
+            }
         }
 
         ensure_sufficient_stack(|| {
@@ -1207,53 +1235,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         span: Span,
         scrutinee_span: Span,
-        start_block: BasicBlock,
+        mut start_block: BasicBlock,
         otherwise_block: BasicBlock,
         candidates: &mut [&mut Candidate<'_, 'tcx>],
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
-        // The candidates are sorted by priority. Check to see whether the
-        // higher priority candidates (and hence at the front of the slice)
-        // have satisfied all their match pairs.
-        let fully_matched = candidates.iter().take_while(|c| c.match_pairs.is_empty()).count();
-        debug!("match_candidates: {:?} candidates fully matched", fully_matched);
-        let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched);
-
-        let block = if !matched_candidates.is_empty() {
-            let otherwise_block =
-                self.select_matched_candidates(matched_candidates, start_block, fake_borrows);
-
-            if let Some(last_otherwise_block) = otherwise_block {
-                last_otherwise_block
-            } else {
-                // Any remaining candidates are unreachable.
-                if unmatched_candidates.is_empty() {
-                    return;
-                }
-                self.cfg.start_new_block()
+        match candidates {
+            [] => {
+                // If there are no candidates that still need testing, we're done. Since all matches are
+                // exhaustive, execution should never reach this point.
+                let source_info = self.source_info(span);
+                self.cfg.goto(start_block, source_info, otherwise_block);
+            }
+            [first, remaining @ ..] if first.match_pairs.is_empty() => {
+                // The first candidate has satisfied all its match pairs; we link it up and continue
+                // with the remaining candidates.
+                start_block = self.select_matched_candidate(first, start_block, fake_borrows);
+                self.match_simplified_candidates(
+                    span,
+                    scrutinee_span,
+                    start_block,
+                    otherwise_block,
+                    remaining,
+                    fake_borrows,
+                )
+            }
+            candidates => {
+                // The first candidate has some unsatisfied match pairs; we proceed to do more tests.
+                self.test_candidates_with_or(
+                    span,
+                    scrutinee_span,
+                    candidates,
+                    start_block,
+                    otherwise_block,
+                    fake_borrows,
+                );
             }
-        } else {
-            start_block
-        };
-
-        // If there are no candidates that still need testing, we're
-        // done. Since all matches are exhaustive, execution should
-        // never reach this point.
-        if unmatched_candidates.is_empty() {
-            let source_info = self.source_info(span);
-            self.cfg.goto(block, source_info, otherwise_block);
-            return;
         }
-
-        // Test for the remaining candidates.
-        self.test_candidates_with_or(
-            span,
-            scrutinee_span,
-            unmatched_candidates,
-            block,
-            otherwise_block,
-            fake_borrows,
-        );
     }
 
     /// Link up matched candidates.
@@ -1275,47 +1293,40 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     /// * the [otherwise block] of the third pattern to a block with an
     ///   [`Unreachable` terminator](TerminatorKind::Unreachable).
     ///
-    /// In addition, we add fake edges from the otherwise blocks to the
+    /// In addition, we later add fake edges from the otherwise blocks to the
     /// pre-binding block of the next candidate in the original set of
     /// candidates.
     ///
     /// [pre-binding block]: Candidate::pre_binding_block
     /// [otherwise block]: Candidate::otherwise_block
-    fn select_matched_candidates(
+    fn select_matched_candidate(
         &mut self,
-        matched_candidates: &mut [&mut Candidate<'_, 'tcx>],
+        candidate: &mut Candidate<'_, 'tcx>,
         start_block: BasicBlock,
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
-    ) -> Option<BasicBlock> {
-        debug_assert!(
-            !matched_candidates.is_empty(),
-            "select_matched_candidates called with no candidates",
-        );
-        debug_assert!(
-            matched_candidates.iter().all(|c| c.subcandidates.is_empty()),
-            "subcandidates should be empty in select_matched_candidates",
-        );
+    ) -> BasicBlock {
+        assert!(candidate.otherwise_block.is_none());
+        assert!(candidate.pre_binding_block.is_none());
+        assert!(candidate.subcandidates.is_empty());
 
-        // Insert a borrows of prefixes of places that are bound and are
-        // behind a dereference projection.
-        //
-        // These borrows are taken to avoid situations like the following:
-        //
-        // match x[10] {
-        //     _ if { x = &[0]; false } => (),
-        //     y => (), // Out of bounds array access!
-        // }
-        //
-        // match *x {
-        //     // y is bound by reference in the guard and then by copy in the
-        //     // arm, so y is 2 in the arm!
-        //     y if { y == 1 && (x = &2) == () } => y,
-        //     _ => 3,
-        // }
         if let Some(fake_borrows) = fake_borrows {
-            for Binding { source, .. } in
-                matched_candidates.iter().flat_map(|candidate| &candidate.bindings)
-            {
+            // Insert a borrows of prefixes of places that are bound and are
+            // behind a dereference projection.
+            //
+            // These borrows are taken to avoid situations like the following:
+            //
+            // match x[10] {
+            //     _ if { x = &[0]; false } => (),
+            //     y => (), // Out of bounds array access!
+            // }
+            //
+            // match *x {
+            //     // y is bound by reference in the guard and then by copy in the
+            //     // arm, so y is 2 in the arm!
+            //     y if { y == 1 && (x = &2) == () } => y,
+            //     _ => 3,
+            // }
+            for Binding { source, .. } in &candidate.bindings {
                 if let Some(i) =
                     source.projection.iter().rposition(|elem| elem == ProjectionElem::Deref)
                 {
@@ -1329,38 +1340,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
         }
 
-        let fully_matched_with_guard = matched_candidates
-            .iter()
-            .position(|c| !c.has_guard)
-            .unwrap_or(matched_candidates.len() - 1);
-
-        let (reachable_candidates, unreachable_candidates) =
-            matched_candidates.split_at_mut(fully_matched_with_guard + 1);
-
-        let mut next_prebinding = start_block;
-
-        for candidate in reachable_candidates.iter_mut() {
-            assert!(candidate.otherwise_block.is_none());
-            assert!(candidate.pre_binding_block.is_none());
-            candidate.pre_binding_block = Some(next_prebinding);
-            if candidate.has_guard {
-                // Create the otherwise block for this candidate, which is the
-                // pre-binding block for the next candidate.
-                next_prebinding = self.cfg.start_new_block();
-                candidate.otherwise_block = Some(next_prebinding);
-            }
-        }
-
-        debug!(
-            "match_candidates: add pre_binding_blocks for unreachable {:?}",
-            unreachable_candidates,
-        );
-        for candidate in unreachable_candidates {
-            assert!(candidate.pre_binding_block.is_none());
-            candidate.pre_binding_block = Some(self.cfg.start_new_block());
+        candidate.pre_binding_block = Some(start_block);
+        let otherwise_block = self.cfg.start_new_block();
+        if candidate.has_guard {
+            // Create the otherwise block for this candidate, which is the
+            // pre-binding block for the next candidate.
+            candidate.otherwise_block = Some(otherwise_block);
         }
-
-        reachable_candidates.last_mut().unwrap().otherwise_block
+        otherwise_block
     }
 
     /// Tests a candidate where there are only or-patterns left to test, or
@@ -1421,51 +1408,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         span: Span,
         scrutinee_span: Span,
         candidates: &mut [&mut Candidate<'_, 'tcx>],
-        block: BasicBlock,
+        start_block: BasicBlock,
         otherwise_block: BasicBlock,
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
         let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
-
-        // All of the or-patterns have been sorted to the end, so if the first
-        // pattern is an or-pattern we only have or-patterns.
-        match first_candidate.match_pairs[0].pattern.kind {
-            PatKind::Or { .. } => (),
-            _ => {
-                self.test_candidates(
-                    span,
-                    scrutinee_span,
-                    candidates,
-                    block,
-                    otherwise_block,
-                    fake_borrows,
-                );
-                return;
-            }
+        assert!(first_candidate.subcandidates.is_empty());
+        if !matches!(first_candidate.match_pairs[0].pattern.kind, PatKind::Or { .. }) {
+            self.test_candidates(
+                span,
+                scrutinee_span,
+                candidates,
+                start_block,
+                otherwise_block,
+                fake_borrows,
+            );
+            return;
         }
 
         let match_pairs = mem::take(&mut first_candidate.match_pairs);
-        first_candidate.pre_binding_block = Some(block);
+        let (first_match_pair, remaining_match_pairs) = match_pairs.split_first().unwrap();
+        let PatKind::Or { ref pats } = &first_match_pair.pattern.kind else { unreachable!() };
 
         let remainder_start = self.cfg.start_new_block();
-        for match_pair in match_pairs {
-            let PatKind::Or { ref pats } = &match_pair.pattern.kind else {
-                bug!("Or-patterns should have been sorted to the end");
-            };
-            let or_span = match_pair.pattern.span;
+        let or_span = first_match_pair.pattern.span;
+        // Test the alternatives of this or-pattern.
+        self.test_or_pattern(
+            first_candidate,
+            start_block,
+            remainder_start,
+            pats,
+            or_span,
+            &first_match_pair.place,
+            fake_borrows,
+        );
 
+        if !remaining_match_pairs.is_empty() {
+            // If more match pairs remain, test them after each subcandidate.
+            // We could add them to the or-candidates before the call to `test_or_pattern` but this
+            // would make it impossible to detect simplifiable or-patterns. That would guarantee
+            // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
             first_candidate.visit_leaves(|leaf_candidate| {
-                self.test_or_pattern(
-                    leaf_candidate,
-                    remainder_start,
-                    pats,
-                    or_span,
-                    &match_pair.place,
+                assert!(leaf_candidate.match_pairs.is_empty());
+                leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
+                let or_start = leaf_candidate.pre_binding_block.unwrap();
+                // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b,
+                // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching
+                // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`.
+                let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start);
+                self.test_candidates_with_or(
+                    span,
+                    scrutinee_span,
+                    &mut [leaf_candidate],
+                    or_start,
+                    or_otherwise,
                     fake_borrows,
                 );
             });
         }
 
+        // Test the remaining candidates.
         self.match_candidates(
             span,
             scrutinee_span,
@@ -1473,17 +1475,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             otherwise_block,
             remaining_candidates,
             fake_borrows,
-        )
+        );
     }
 
     #[instrument(
-        skip(self, otherwise, or_span, place, fake_borrows, candidate, pats),
+        skip(self, start_block, otherwise_block, or_span, place, fake_borrows, candidate, pats),
         level = "debug"
     )]
     fn test_or_pattern<'pat>(
         &mut self,
         candidate: &mut Candidate<'pat, 'tcx>,
-        otherwise: BasicBlock,
+        start_block: BasicBlock,
+        otherwise_block: BasicBlock,
         pats: &'pat [Box<Pat<'tcx>>],
         or_span: Span,
         place: &PlaceBuilder<'tcx>,
@@ -1495,16 +1498,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             .map(|pat| Candidate::new(place.clone(), pat, candidate.has_guard, self))
             .collect();
         let mut or_candidate_refs: Vec<_> = or_candidates.iter_mut().collect();
-        let otherwise = if let Some(otherwise_block) = candidate.otherwise_block {
-            otherwise_block
-        } else {
-            otherwise
-        };
         self.match_candidates(
             or_span,
             or_span,
-            candidate.pre_binding_block.unwrap(),
-            otherwise,
+            start_block,
+            otherwise_block,
             &mut or_candidate_refs,
             fake_borrows,
         );
diff --git a/compiler/rustc_mir_build/src/build/matches/simplify.rs b/compiler/rustc_mir_build/src/build/matches/simplify.rs
index dba8e8b6499..83922dce327 100644
--- a/compiler/rustc_mir_build/src/build/matches/simplify.rs
+++ b/compiler/rustc_mir_build/src/build/matches/simplify.rs
@@ -6,7 +6,7 @@
 //! - `place @ (P1, P2)` can be simplified to `[place.0 @ P1, place.1 @ P2]`
 //! - `place @ x` can be simplified to `[]` by binding `x` to `place`
 //!
-//! The `simplify_candidate` routine just repeatedly applies these
+//! The `simplify_match_pairs` routine just repeatedly applies these
 //! sort of simplifications until there is nothing left to
 //! simplify. Match pairs cannot be simplified if they require some
 //! sort of test: for example, testing which variant an enum is, or
@@ -22,25 +22,15 @@ use rustc_middle::ty;
 use std::mem;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
-    /// Simplify a candidate so that all match pairs require a test.
-    ///
-    /// This method will also split a candidate, in which the only
-    /// match-pair is an or-pattern, into multiple candidates.
-    /// This is so that
-    ///
-    /// match x {
-    ///     0 | 1 => { ... },
-    ///     2 | 3 => { ... },
-    /// }
-    ///
-    /// only generates a single switch. If this happens this method returns
-    /// `true`.
-    #[instrument(skip(self, candidate), level = "debug")]
-    pub(super) fn simplify_candidate<'pat>(
+    /// Simplify a list of match pairs so they all require a test. Stores relevant bindings and
+    /// ascriptions in the provided `Vec`s.
+    #[instrument(skip(self), level = "debug")]
+    pub(super) fn simplify_match_pairs<'pat>(
         &mut self,
-        candidate: &mut Candidate<'pat, 'tcx>,
-    ) -> bool {
-        debug!("{candidate:#?}");
+        match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
+        candidate_bindings: &mut Vec<Binding<'tcx>>,
+        candidate_ascriptions: &mut Vec<Ascription<'tcx>>,
+    ) {
         // In order to please the borrow checker, in a pattern like `x @ pat` we must lower the
         // bindings in `pat` before `x`. E.g. (#69971):
         //
@@ -68,105 +58,96 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // bindings in iter 2: [6, 7]
         //
         // final bindings: [6, 7, 4, 5, 1, 2, 3]
-        let mut accumulated_bindings = mem::take(&mut candidate.bindings);
-        // Repeatedly simplify match pairs until fixed point is reached
+        let mut accumulated_bindings = mem::take(candidate_bindings);
+        let mut simplified_match_pairs = Vec::new();
+        // Repeatedly simplify match pairs until we're left with only unsimplifiable ones.
         loop {
-            let mut changed = false;
-            for match_pair in mem::take(&mut candidate.match_pairs) {
-                match self.simplify_match_pair(match_pair, candidate) {
-                    Ok(()) => {
-                        changed = true;
-                    }
-                    Err(match_pair) => {
-                        candidate.match_pairs.push(match_pair);
-                    }
+            for match_pair in mem::take(match_pairs) {
+                if let Err(match_pair) = self.simplify_match_pair(
+                    match_pair,
+                    candidate_bindings,
+                    candidate_ascriptions,
+                    match_pairs,
+                ) {
+                    simplified_match_pairs.push(match_pair);
                 }
             }
 
             // This does: accumulated_bindings = candidate.bindings.take() ++ accumulated_bindings
-            candidate.bindings.extend_from_slice(&accumulated_bindings);
-            mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
-            candidate.bindings.clear();
+            candidate_bindings.extend_from_slice(&accumulated_bindings);
+            mem::swap(candidate_bindings, &mut accumulated_bindings);
+            candidate_bindings.clear();
 
-            if !changed {
-                // If we were not able to simplify anymore, done.
+            if match_pairs.is_empty() {
                 break;
             }
         }
 
-        // Store computed bindings back in `candidate`.
-        mem::swap(&mut candidate.bindings, &mut accumulated_bindings);
-
-        let did_expand_or =
-            if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place }] =
-                &*candidate.match_pairs
-            {
-                candidate.subcandidates = self.create_or_subcandidates(candidate, place, pats);
-                candidate.match_pairs.clear();
-                true
-            } else {
-                false
-            };
+        // Store computed bindings back in `candidate_bindings`.
+        mem::swap(candidate_bindings, &mut accumulated_bindings);
+        // Store simplified match pairs back in `match_pairs`.
+        mem::swap(match_pairs, &mut simplified_match_pairs);
 
         // Move or-patterns to the end, because they can result in us
         // creating additional candidates, so we want to test them as
         // late as possible.
-        candidate.match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
-        debug!(simplified = ?candidate, "simplify_candidate");
-
-        did_expand_or
+        match_pairs.sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
+        debug!(simplified = ?match_pairs, "simplify_match_pairs");
     }
 
-    /// Given `candidate` that has a single or-pattern for its match-pairs,
-    /// creates a fresh candidate for each of its input subpatterns passed via
-    /// `pats`.
-    fn create_or_subcandidates<'pat>(
+    /// Create a new candidate for each pattern in `pats`, and recursively simplify tje
+    /// single-or-pattern case.
+    pub(super) fn create_or_subcandidates<'pat>(
         &mut self,
-        candidate: &Candidate<'pat, 'tcx>,
         place: &PlaceBuilder<'tcx>,
         pats: &'pat [Box<Pat<'tcx>>],
+        has_guard: bool,
     ) -> Vec<Candidate<'pat, 'tcx>> {
         pats.iter()
             .map(|box pat| {
-                let mut candidate = Candidate::new(place.clone(), pat, candidate.has_guard, self);
-                self.simplify_candidate(&mut candidate);
+                let mut candidate = Candidate::new(place.clone(), pat, has_guard, self);
+                if let [MatchPair { pattern: Pat { kind: PatKind::Or { pats }, .. }, place, .. }] =
+                    &*candidate.match_pairs
+                {
+                    candidate.subcandidates =
+                        self.create_or_subcandidates(place, pats, candidate.has_guard);
+                    candidate.match_pairs.pop();
+                }
                 candidate
             })
             .collect()
     }
 
-    /// Tries to simplify `match_pair`, returning `Ok(())` if
-    /// successful. If successful, new match pairs and bindings will
-    /// have been pushed into the candidate. If no simplification is
-    /// possible, `Err` is returned and no changes are made to
-    /// candidate.
+    /// Tries to simplify `match_pair`, returning `Ok(())` if successful. If successful, new match
+    /// pairs and bindings will have been pushed into the respective `Vec`s. If no simplification is
+    /// possible, `Err` is returned.
     fn simplify_match_pair<'pat>(
         &mut self,
-        match_pair: MatchPair<'pat, 'tcx>,
-        candidate: &mut Candidate<'pat, 'tcx>,
+        mut match_pair: MatchPair<'pat, 'tcx>,
+        bindings: &mut Vec<Binding<'tcx>>,
+        ascriptions: &mut Vec<Ascription<'tcx>>,
+        match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
     ) -> Result<(), MatchPair<'pat, 'tcx>> {
         match match_pair.pattern.kind {
+            PatKind::Leaf { .. }
+            | PatKind::Deref { .. }
+            | PatKind::Array { .. }
+            | PatKind::Never
+            | PatKind::Wild
+            | PatKind::Error(_) => {}
+
             PatKind::AscribeUserType {
-                ref subpattern,
                 ascription: thir::Ascription { ref annotation, variance },
+                ..
             } => {
                 // Apply the type ascription to the value at `match_pair.place`
                 if let Some(source) = match_pair.place.try_to_place(self) {
-                    candidate.ascriptions.push(Ascription {
+                    ascriptions.push(Ascription {
                         annotation: annotation.clone(),
                         source,
                         variance,
                     });
                 }
-
-                candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
-
-                Ok(())
-            }
-
-            PatKind::Wild | PatKind::Error(_) => {
-                // nothing left to do
-                Ok(())
             }
 
             PatKind::Binding {
@@ -175,35 +156,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 mode,
                 var,
                 ty: _,
-                ref subpattern,
+                subpattern: _,
                 is_primary: _,
             } => {
                 if let Some(source) = match_pair.place.try_to_place(self) {
-                    candidate.bindings.push(Binding {
+                    bindings.push(Binding {
                         span: match_pair.pattern.span,
                         source,
                         var_id: var,
                         binding_mode: mode,
                     });
                 }
-
-                if let Some(subpattern) = subpattern.as_ref() {
-                    // this is the `x @ P` case; have to keep matching against `P` now
-                    candidate.match_pairs.push(MatchPair::new(match_pair.place, subpattern, self));
-                }
-
-                Ok(())
-            }
-
-            PatKind::Never => {
-                // A never pattern acts like a load from the place.
-                // FIXME(never_patterns): load from the place
-                Ok(())
-            }
-
-            PatKind::Constant { .. } => {
-                // FIXME normalize patterns when possible
-                Err(match_pair)
             }
 
             PatKind::InlineConstant { subpattern: ref pattern, def } => {
@@ -232,42 +195,33 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         span,
                         user_ty: Box::new(user_ty),
                     };
-                    candidate.ascriptions.push(Ascription {
+                    ascriptions.push(Ascription {
                         annotation,
                         source,
                         variance: ty::Contravariant,
                     });
                 }
-                candidate.match_pairs.push(MatchPair::new(match_pair.place, pattern, self));
+            }
 
-                Ok(())
+            PatKind::Constant { .. } => {
+                // FIXME normalize patterns when possible
+                return Err(match_pair);
             }
 
             PatKind::Range(ref range) => {
-                if let Some(true) = range.is_full_range(self.tcx) {
-                    // Irrefutable pattern match.
-                    return Ok(());
+                if range.is_full_range(self.tcx) != Some(true) {
+                    return Err(match_pair);
                 }
-                Err(match_pair)
             }
 
             PatKind::Slice { ref prefix, ref slice, ref suffix } => {
-                if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
-                    // irrefutable
-                    self.prefix_slice_suffix(
-                        &mut candidate.match_pairs,
-                        &match_pair.place,
-                        prefix,
-                        slice,
-                        suffix,
-                    );
-                    Ok(())
-                } else {
-                    Err(match_pair)
+                if !(prefix.is_empty() && slice.is_some() && suffix.is_empty()) {
+                    self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
+                    return Err(match_pair);
                 }
             }
 
-            PatKind::Variant { adt_def, args, variant_index, ref subpatterns } => {
+            PatKind::Variant { adt_def, args, variant_index, subpatterns: _ } => {
                 let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
                     i == variant_index || {
                         (self.tcx.features().exhaustive_patterns
@@ -279,41 +233,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     }
                 }) && (adt_def.did().is_local()
                     || !adt_def.is_variant_list_non_exhaustive());
-                if irrefutable {
-                    let place_builder = match_pair.place.downcast(adt_def, variant_index);
-                    candidate
-                        .match_pairs
-                        .extend(self.field_match_pairs(place_builder, subpatterns));
-                    Ok(())
-                } else {
-                    Err(match_pair)
+                if !irrefutable {
+                    self.simplify_match_pairs(&mut match_pair.subpairs, bindings, ascriptions);
+                    return Err(match_pair);
                 }
             }
 
-            PatKind::Array { ref prefix, ref slice, ref suffix } => {
-                self.prefix_slice_suffix(
-                    &mut candidate.match_pairs,
-                    &match_pair.place,
-                    prefix,
-                    slice,
-                    suffix,
-                );
-                Ok(())
-            }
-
-            PatKind::Leaf { ref subpatterns } => {
-                // tuple struct, match subpats (if any)
-                candidate.match_pairs.extend(self.field_match_pairs(match_pair.place, subpatterns));
-                Ok(())
-            }
-
-            PatKind::Deref { ref subpattern } => {
-                let place_builder = match_pair.place.deref();
-                candidate.match_pairs.push(MatchPair::new(place_builder, subpattern, self));
-                Ok(())
-            }
-
-            PatKind::Or { .. } => Err(match_pair),
+            PatKind::Or { .. } => return Err(match_pair),
         }
+
+        // Simplifiable pattern; we replace it with its subpairs.
+        match_pairs.append(&mut match_pair.subpairs);
+        Ok(())
     }
 }
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index 990be30b2d6..ae9ebe7170b 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -590,25 +590,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let (match_pair_index, match_pair) =
             candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == *test_place)?;
 
-        match (&test.kind, &match_pair.pattern.kind) {
+        let fully_matched;
+        let ret = match (&test.kind, &match_pair.pattern.kind) {
             // If we are performing a variant switch, then this
             // informs variant patterns, but nothing else.
             (
                 &TestKind::Switch { adt_def: tested_adt_def, .. },
-                &PatKind::Variant { adt_def, variant_index, ref subpatterns, .. },
+                &PatKind::Variant { adt_def, variant_index, .. },
             ) => {
                 assert_eq!(adt_def, tested_adt_def);
-                self.candidate_after_variant_switch(
-                    match_pair_index,
-                    adt_def,
-                    variant_index,
-                    subpatterns,
-                    candidate,
-                );
+                fully_matched = true;
                 Some(variant_index.as_usize())
             }
-
-            (&TestKind::Switch { .. }, _) => None,
+            (&TestKind::Switch { .. }, _) => {
+                fully_matched = false;
+                None
+            }
 
             // If we are performing a switch over integers, then this informs integer
             // equality, but nothing else.
@@ -618,12 +615,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Constant { value })
                 if is_switch_ty(match_pair.pattern.ty) =>
             {
+                fully_matched = true;
                 let index = options.get_index_of(value).unwrap();
-                self.candidate_without_match_pair(match_pair_index, candidate);
                 Some(index)
             }
-
             (TestKind::SwitchInt { switch_ty: _, options }, PatKind::Range(range)) => {
+                fully_matched = false;
                 let not_contained =
                     self.values_not_contained_in_range(&*range, options).unwrap_or(false);
 
@@ -633,8 +630,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     options.len()
                 })
             }
-
-            (&TestKind::SwitchInt { .. }, _) => None,
+            (&TestKind::SwitchInt { .. }, _) => {
+                fully_matched = false;
+                None
+            }
 
             (
                 &TestKind::Len { len: test_len, op: BinOp::Eq },
@@ -645,34 +644,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     (Ordering::Equal, &None) => {
                         // on true, min_len = len = $actual_length,
                         // on false, len != $actual_length
-                        self.candidate_after_slice_test(
-                            match_pair_index,
-                            candidate,
-                            prefix,
-                            slice,
-                            suffix,
-                        );
+                        fully_matched = true;
                         Some(0)
                     }
                     (Ordering::Less, _) => {
                         // test_len < pat_len. If $actual_len = test_len,
                         // then $actual_len < pat_len and we don't have
                         // enough elements.
+                        fully_matched = false;
                         Some(1)
                     }
                     (Ordering::Equal | Ordering::Greater, &Some(_)) => {
                         // This can match both if $actual_len = test_len >= pat_len,
                         // and if $actual_len > test_len. We can't advance.
+                        fully_matched = false;
                         None
                     }
                     (Ordering::Greater, &None) => {
                         // test_len != pat_len, so if $actual_len = test_len, then
                         // $actual_len != pat_len.
+                        fully_matched = false;
                         Some(1)
                     }
                 }
             }
-
             (
                 &TestKind::Len { len: test_len, op: BinOp::Ge },
                 PatKind::Slice { prefix, slice, suffix },
@@ -683,29 +678,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     (Ordering::Equal, &Some(_)) => {
                         // $actual_len >= test_len = pat_len,
                         // so we can match.
-                        self.candidate_after_slice_test(
-                            match_pair_index,
-                            candidate,
-                            prefix,
-                            slice,
-                            suffix,
-                        );
+                        fully_matched = true;
                         Some(0)
                     }
                     (Ordering::Less, _) | (Ordering::Equal, &None) => {
                         // test_len <= pat_len. If $actual_len < test_len,
                         // then it is also < pat_len, so the test passing is
                         // necessary (but insufficient).
+                        fully_matched = false;
                         Some(0)
                     }
                     (Ordering::Greater, &None) => {
                         // test_len > pat_len. If $actual_len >= test_len > pat_len,
                         // then we know we won't have a match.
+                        fully_matched = false;
                         Some(1)
                     }
                     (Ordering::Greater, &Some(_)) => {
                         // test_len < pat_len, and is therefore less
                         // strict. This can still go both ways.
+                        fully_matched = false;
                         None
                     }
                 }
@@ -713,16 +705,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
             (TestKind::Range(test), PatKind::Range(pat)) => {
                 if test == pat {
-                    self.candidate_without_match_pair(match_pair_index, candidate);
-                    return Some(0);
+                    fully_matched = true;
+                    Some(0)
+                } else {
+                    fully_matched = false;
+                    // If the testing range does not overlap with pattern range,
+                    // the pattern can be matched only if this test fails.
+                    if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
                 }
-
-                // If the testing range does not overlap with pattern range,
-                // the pattern can be matched only if this test fails.
-                if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
             }
-
             (TestKind::Range(range), &PatKind::Constant { value }) => {
+                fully_matched = false;
                 if !range.contains(value, self.tcx, self.param_env)? {
                     // `value` is not contained in the testing range,
                     // so `value` can be matched only if this test fails.
@@ -731,8 +724,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     None
                 }
             }
-
-            (&TestKind::Range { .. }, _) => None,
+            (&TestKind::Range { .. }, _) => {
+                fully_matched = false;
+                None
+            }
 
             (&TestKind::Eq { .. } | &TestKind::Len { .. }, _) => {
                 // The call to `self.test(&match_pair)` below is not actually used to generate any
@@ -751,64 +746,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // FIXME(#29623) we can be more clever here
                 let pattern_test = self.test(match_pair);
                 if pattern_test.kind == test.kind {
-                    self.candidate_without_match_pair(match_pair_index, candidate);
+                    fully_matched = true;
                     Some(0)
                 } else {
+                    fully_matched = false;
                     None
                 }
             }
-        }
-    }
-
-    fn candidate_without_match_pair(
-        &mut self,
-        match_pair_index: usize,
-        candidate: &mut Candidate<'_, 'tcx>,
-    ) {
-        candidate.match_pairs.remove(match_pair_index);
-    }
+        };
 
-    fn candidate_after_slice_test<'pat>(
-        &mut self,
-        match_pair_index: usize,
-        candidate: &mut Candidate<'pat, 'tcx>,
-        prefix: &'pat [Box<Pat<'tcx>>],
-        opt_slice: &'pat Option<Box<Pat<'tcx>>>,
-        suffix: &'pat [Box<Pat<'tcx>>],
-    ) {
-        let removed_place = candidate.match_pairs.remove(match_pair_index).place;
-        self.prefix_slice_suffix(
-            &mut candidate.match_pairs,
-            &removed_place,
-            prefix,
-            opt_slice,
-            suffix,
-        );
-    }
+        if fully_matched {
+            // Replace the match pair by its sub-pairs.
+            let match_pair = candidate.match_pairs.remove(match_pair_index);
+            candidate.match_pairs.extend(match_pair.subpairs);
+            // Move or-patterns to the end.
+            candidate
+                .match_pairs
+                .sort_by_key(|pair| matches!(pair.pattern.kind, PatKind::Or { .. }));
+        }
 
-    fn candidate_after_variant_switch<'pat>(
-        &mut self,
-        match_pair_index: usize,
-        adt_def: ty::AdtDef<'tcx>,
-        variant_index: VariantIdx,
-        subpatterns: &'pat [FieldPat<'tcx>],
-        candidate: &mut Candidate<'pat, 'tcx>,
-    ) {
-        let match_pair = candidate.match_pairs.remove(match_pair_index);
-
-        // So, if we have a match-pattern like `x @ Enum::Variant(P1, P2)`,
-        // we want to create a set of derived match-patterns like
-        // `(x as Variant).0 @ P1` and `(x as Variant).1 @ P1`.
-        let downcast_place = match_pair.place.downcast(adt_def, variant_index); // `(x as Variant)`
-        let consequent_match_pairs = subpatterns.iter().map(|subpattern| {
-            // e.g., `(x as Variant).0`
-            let place = downcast_place
-                .clone_project(PlaceElem::Field(subpattern.field, subpattern.pattern.ty));
-            // e.g., `(x as Variant).0 @ P1`
-            MatchPair::new(place, &subpattern.pattern, self)
-        });
-
-        candidate.match_pairs.extend(consequent_match_pairs);
+        ret
     }
 
     fn error_simplifiable<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> ! {
diff --git a/compiler/rustc_mir_build/src/build/matches/util.rs b/compiler/rustc_mir_build/src/build/matches/util.rs
index c34105174ef..e42d764147c 100644
--- a/compiler/rustc_mir_build/src/build/matches/util.rs
+++ b/compiler/rustc_mir_build/src/build/matches/util.rs
@@ -6,7 +6,6 @@ use rustc_middle::mir::*;
 use rustc_middle::thir::*;
 use rustc_middle::ty;
 use rustc_middle::ty::TypeVisitableExt;
-use smallvec::SmallVec;
 
 impl<'a, 'tcx> Builder<'a, 'tcx> {
     pub(crate) fn field_match_pairs<'pat>(
@@ -26,7 +25,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
     pub(crate) fn prefix_slice_suffix<'pat>(
         &mut self,
-        match_pairs: &mut SmallVec<[MatchPair<'pat, 'tcx>; 1]>,
+        match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
         place: &PlaceBuilder<'tcx>,
         prefix: &'pat [Box<Pat<'tcx>>],
         opt_slice: &'pat Option<Box<Pat<'tcx>>>,
@@ -97,7 +96,7 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
     pub(in crate::build) fn new(
         mut place: PlaceBuilder<'tcx>,
         pattern: &'pat Pat<'tcx>,
-        cx: &Builder<'_, 'tcx>,
+        cx: &mut Builder<'_, 'tcx>,
     ) -> MatchPair<'pat, 'tcx> {
         // Force the place type to the pattern's type.
         // FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
@@ -117,6 +116,51 @@ impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
         if may_need_cast {
             place = place.project(ProjectionElem::OpaqueCast(pattern.ty));
         }
-        MatchPair { place, pattern }
+
+        let mut subpairs = Vec::new();
+        match pattern.kind {
+            PatKind::Constant { .. }
+            | PatKind::Range(_)
+            | PatKind::Or { .. }
+            | PatKind::Never
+            | PatKind::Wild
+            | PatKind::Error(_) => {}
+
+            PatKind::AscribeUserType { ref subpattern, .. } => {
+                subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
+            }
+
+            PatKind::Binding { ref subpattern, .. } => {
+                if let Some(subpattern) = subpattern.as_ref() {
+                    // this is the `x @ P` case; have to keep matching against `P` now
+                    subpairs.push(MatchPair::new(place.clone(), subpattern, cx));
+                }
+            }
+
+            PatKind::InlineConstant { subpattern: ref pattern, .. } => {
+                subpairs.push(MatchPair::new(place.clone(), pattern, cx));
+            }
+
+            PatKind::Slice { ref prefix, ref slice, ref suffix }
+            | PatKind::Array { ref prefix, ref slice, ref suffix } => {
+                cx.prefix_slice_suffix(&mut subpairs, &place, prefix, slice, suffix);
+            }
+
+            PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => {
+                let downcast_place = place.clone().downcast(adt_def, variant_index); // `(x as Variant)`
+                subpairs = cx.field_match_pairs(downcast_place, subpatterns);
+            }
+
+            PatKind::Leaf { ref subpatterns } => {
+                subpairs = cx.field_match_pairs(place.clone(), subpatterns);
+            }
+
+            PatKind::Deref { ref subpattern } => {
+                let place_builder = place.clone().deref();
+                subpairs.push(MatchPair::new(place_builder, subpattern, cx));
+            }
+        }
+
+        MatchPair { place, pattern, subpairs }
     }
 }
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 0bc5fe6ef89..2a42dae289b 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1,7 +1,7 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::DiagnosticArgValue;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee,
     IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp,
 };
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -420,7 +420,11 @@ pub struct UnsafeNotInheritedLintNote {
 }
 
 impl AddToDiagnostic for UnsafeNotInheritedLintNote {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body);
         let body_start = self.body_span.shrink_to_lo();
         let body_end = self.body_span.shrink_to_hi();
@@ -863,7 +867,11 @@ pub struct Variant {
 }
 
 impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.arg("ty", self.ty);
         let mut spans = MultiSpan::from(self.adt_def_span);
 
diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs
index d444de8b28e..65cc13286af 100644
--- a/compiler/rustc_mir_build/src/thir/constant.rs
+++ b/compiler/rustc_mir_build/src/thir/constant.rs
@@ -12,15 +12,12 @@ pub(crate) fn lit_to_const<'tcx>(
 
     let trunc = |n| {
         let param_ty = ParamEnv::reveal_all().and(ty);
-        let width =
-            tcx.layout_of(param_ty)
-                .map_err(|_| {
-                    LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
-                        "couldn't compute width of literal: {:?}",
-                        lit_input.lit
-                    )))
-                })?
-                .size;
+        let width = match tcx.layout_of(param_ty) {
+            Ok(layout) => layout.size,
+            Err(_) => {
+                tcx.dcx().bug(format!("couldn't compute width of literal: {:?}", lit_input.lit))
+            }
+        };
         trace!("trunc {} with size {} and shift {}", n, width.bits(), 128 - width.bits());
         let result = width.truncate(n);
         trace!("trunc result: {}", result);
@@ -59,15 +56,11 @@ pub(crate) fn lit_to_const<'tcx>(
         }
         (ast::LitKind::Bool(b), ty::Bool) => ty::ValTree::from_scalar_int((*b).into()),
         (ast::LitKind::Float(n, _), ty::Float(fty)) => {
-            let bits =
-                parse_float_into_scalar(*n, *fty, neg)
-                    .ok_or_else(|| {
-                        LitToConstError::Reported(tcx.dcx().delayed_bug(format!(
-                            "couldn't parse float literal: {:?}",
-                            lit_input.lit
-                        )))
-                    })?
-                    .assert_int();
+            let bits = parse_float_into_scalar(*n, *fty, neg)
+                .ok_or_else(|| {
+                    tcx.dcx().bug(format!("couldn't parse float literal: {:?}", lit_input.lit))
+                })?
+                .assert_int();
             ty::ValTree::from_scalar_int(bits)
         }
         (ast::LitKind::Char(c), ty::Char) => ty::ValTree::from_scalar_int((*c).into()),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 1a5cf13e289..0329e1d3096 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -175,7 +175,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
     ) -> Result<PatKind<'tcx>, ErrorGuaranteed> {
         if lo_expr.is_none() && hi_expr.is_none() {
             let msg = "found twice-open range pattern (`..`) outside of error recovery";
-            return Err(self.tcx.dcx().span_delayed_bug(span, msg));
+            self.tcx.dcx().span_bug(span, msg);
         }
 
         let (lo, lo_ascr, lo_inline) = self.lower_pattern_range_endpoint(lo_expr)?;
diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs
index a0851aa557b..54b13a40e92 100644
--- a/compiler/rustc_mir_transform/src/coroutine.rs
+++ b/compiler/rustc_mir_transform/src/coroutine.rs
@@ -1615,11 +1615,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
                 (args.discr_ty(tcx), coroutine_kind.movability() == hir::Movability::Movable)
             }
             _ => {
-                tcx.dcx().span_delayed_bug(
-                    body.span,
-                    format!("unexpected coroutine type {coroutine_ty}"),
-                );
-                return;
+                tcx.dcx().span_bug(body.span, format!("unexpected coroutine type {coroutine_ty}"));
             }
         };
 
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index 934e77e7deb..98fb1d8e1c9 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -61,7 +61,7 @@ pub(super) fn generate_coverage_spans(
             hir_info,
             basic_coverage_blocks,
         );
-        let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans);
+        let coverage_spans = SpansRefiner::refine_sorted_spans(sorted_spans);
         mappings.extend(coverage_spans.into_iter().map(|RefinedCovspan { bcb, span, .. }| {
             // Each span produced by the generator represents an ordinary code region.
             BcbMapping { kind: BcbMappingKind::Code(bcb), span }
@@ -88,8 +88,6 @@ pub(super) fn generate_coverage_spans(
 
 #[derive(Debug)]
 struct CurrCovspan {
-    /// This is used as the basis for [`PrevCovspan::original_span`], so it must
-    /// not be modified.
     span: Span,
     bcb: BasicCoverageBlock,
     is_closure: bool,
@@ -102,7 +100,7 @@ impl CurrCovspan {
 
     fn into_prev(self) -> PrevCovspan {
         let Self { span, bcb, is_closure } = self;
-        PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure }
+        PrevCovspan { span, bcb, merged_spans: vec![span], is_closure }
     }
 
     fn into_refined(self) -> RefinedCovspan {
@@ -115,7 +113,6 @@ impl CurrCovspan {
 
 #[derive(Debug)]
 struct PrevCovspan {
-    original_span: Span,
     span: Span,
     bcb: BasicCoverageBlock,
     /// List of all the original spans from MIR that have been merged into this
@@ -135,42 +132,17 @@ impl PrevCovspan {
         self.merged_spans.push(other.span);
     }
 
-    fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
+    fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option<RefinedCovspan> {
         self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
         if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
             self.span = self.span.with_hi(max_hi);
         }
-    }
-
-    fn into_dup(self) -> DuplicateCovspan {
-        let Self { original_span, span, bcb, merged_spans: _, is_closure } = self;
-        // Only unmodified spans end up in `pending_dups`.
-        debug_assert_eq!(original_span, span);
-        DuplicateCovspan { span, bcb, is_closure }
-    }
-
-    fn refined_copy(&self) -> RefinedCovspan {
-        let &Self { original_span: _, span, bcb, merged_spans: _, is_closure } = self;
-        RefinedCovspan { span, bcb, is_closure }
-    }
 
-    fn into_refined(self) -> RefinedCovspan {
-        self.refined_copy()
+        if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) }
     }
-}
-
-#[derive(Debug)]
-struct DuplicateCovspan {
-    span: Span,
-    bcb: BasicCoverageBlock,
-    is_closure: bool,
-}
 
-impl DuplicateCovspan {
-    /// Returns a copy of this covspan, as a [`RefinedCovspan`].
-    /// Should only be called in places that would otherwise clone this covspan.
     fn refined_copy(&self) -> RefinedCovspan {
-        let &Self { span, bcb, is_closure } = self;
+        let &Self { span, bcb, merged_spans: _, is_closure } = self;
         RefinedCovspan { span, bcb, is_closure }
     }
 
@@ -205,10 +177,7 @@ impl RefinedCovspan {
 ///  * Merge spans that represent continuous (both in source code and control flow), non-branching
 ///    execution
 ///  * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
-struct SpansRefiner<'a> {
-    /// The BasicCoverageBlock Control Flow Graph (BCB CFG).
-    basic_coverage_blocks: &'a CoverageGraph,
-
+struct SpansRefiner {
     /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
     /// dominance between the `BasicCoverageBlock`s of equal `Span`s.
     sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
@@ -223,36 +192,22 @@ struct SpansRefiner<'a> {
     /// If that `curr` was discarded, `prev` retains its value from the previous iteration.
     some_prev: Option<PrevCovspan>,
 
-    /// One or more coverage spans with the same `Span` but different `BasicCoverageBlock`s, and
-    /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list.
-    /// If a new `curr` span also fits this criteria (compared to an existing list of
-    /// `pending_dups`), that `curr` moves to `prev` before possibly being added to
-    /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups`
-    /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev`
-    /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a
-    /// `prev` with a matching `Span`)
-    pending_dups: Vec<DuplicateCovspan>,
-
     /// The final coverage spans to add to the coverage map. A `Counter` or `Expression`
     /// will also be injected into the MIR for each BCB that has associated spans.
     refined_spans: Vec<RefinedCovspan>,
 }
 
-impl<'a> SpansRefiner<'a> {
+impl SpansRefiner {
     /// Takes the initial list of (sorted) spans extracted from MIR, and "refines"
     /// them by merging compatible adjacent spans, removing redundant spans,
     /// and carving holes in spans when they overlap in unwanted ways.
-    fn refine_sorted_spans(
-        basic_coverage_blocks: &'a CoverageGraph,
-        sorted_spans: Vec<SpanFromMir>,
-    ) -> Vec<RefinedCovspan> {
+    fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
+        let sorted_spans_len = sorted_spans.len();
         let this = Self {
-            basic_coverage_blocks,
             sorted_spans_iter: sorted_spans.into_iter(),
             some_curr: None,
             some_prev: None,
-            pending_dups: Vec::new(),
-            refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
+            refined_spans: Vec::with_capacity(sorted_spans_len),
         };
 
         this.to_refined_spans()
@@ -292,21 +247,11 @@ impl<'a> SpansRefiner<'a> {
                 self.take_curr(); // Discards curr.
             } else if curr.is_closure {
                 self.carve_out_span_for_closure();
-            } else if prev.original_span == prev.span && prev.span == curr.span {
-                // Prev and curr have the same span, and prev's span hasn't
-                // been modified by other spans.
-                self.update_pending_dups();
             } else {
                 self.cutoff_prev_at_overlapping_curr();
             }
         }
 
-        // Drain any remaining dups into the output.
-        for dup in self.pending_dups.drain(..) {
-            debug!("    ...adding at least one pending dup={:?}", dup);
-            self.refined_spans.push(dup.into_refined());
-        }
-
         // There is usually a final span remaining in `prev` after the loop ends,
         // so add it to the output as well.
         if let Some(prev) = self.some_prev.take() {
@@ -359,36 +304,6 @@ impl<'a> SpansRefiner<'a> {
         self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)"))
     }
 
-    /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the
-    /// `pending_dups` spans), then one of the following two things happened during the previous
-    /// iteration:
-    ///   * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups
-    ///     (in which case there should be at least two spans in `pending_dups`); or
-    ///   * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case
-    ///     `pending_dups` could have as few as one span)
-    /// In either case, no more spans will match the span of `pending_dups`, so
-    /// add the `pending_dups` if they don't overlap `curr`, and clear the list.
-    fn maybe_flush_pending_dups(&mut self) {
-        let Some(last_dup) = self.pending_dups.last() else { return };
-        if last_dup.span == self.prev().span {
-            return;
-        }
-
-        debug!(
-            "    SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \
-            previous iteration, or prev started a new disjoint span"
-        );
-        if last_dup.span.hi() <= self.curr().span.lo() {
-            for dup in self.pending_dups.drain(..) {
-                debug!("    ...adding at least one pending={:?}", dup);
-                self.refined_spans.push(dup.into_refined());
-            }
-        } else {
-            self.pending_dups.clear();
-        }
-        assert!(self.pending_dups.is_empty());
-    }
-
     /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order.
     fn next_coverage_span(&mut self) -> bool {
         if let Some(curr) = self.some_curr.take() {
@@ -408,7 +323,6 @@ impl<'a> SpansRefiner<'a> {
                 );
             } else {
                 self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure));
-                self.maybe_flush_pending_dups();
                 return true;
             }
         }
@@ -433,13 +347,6 @@ impl<'a> SpansRefiner<'a> {
             let mut pre_closure = self.prev().refined_copy();
             pre_closure.span = pre_closure.span.with_hi(left_cutoff);
             debug!("  prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure);
-
-            for mut dup in self.pending_dups.iter().map(DuplicateCovspan::refined_copy) {
-                dup.span = dup.span.with_hi(left_cutoff);
-                debug!("    ...and at least one pre_closure dup={:?}", dup);
-                self.refined_spans.push(dup);
-            }
-
             self.refined_spans.push(pre_closure);
         }
 
@@ -448,58 +355,9 @@ impl<'a> SpansRefiner<'a> {
             self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
             debug!("  Mutated prev.span to start after the closure. prev={:?}", self.prev());
 
-            for dup in &mut self.pending_dups {
-                debug!("    ...and at least one overlapping dup={:?}", dup);
-                dup.span = dup.span.with_lo(right_cutoff);
-            }
-
             // Prevent this curr from becoming prev.
             let closure_covspan = self.take_curr().into_refined();
             self.refined_spans.push(closure_covspan); // since self.prev() was already updated
-        } else {
-            self.pending_dups.clear();
-        }
-    }
-
-    /// Called if `curr.span` equals `prev.original_span` (and potentially equal to all
-    /// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed.
-    /// If prev.span() was merged into other spans (with matching BCB, for instance),
-    /// `prev.span.hi()` will be greater than (further right of) `prev.original_span.hi()`.
-    /// If prev.span() was split off to the right of a closure, prev.span().lo() will be
-    /// greater than prev.original_span.lo(). The actual span of `prev.original_span` is
-    /// not as important as knowing that `prev()` **used to have the same span** as `curr()`,
-    /// which means their sort order is still meaningful for determining the dominator
-    /// relationship.
-    ///
-    /// When two coverage spans have the same `Span`, dominated spans can be discarded; but if
-    /// neither coverage span dominates the other, both (or possibly more than two) are held,
-    /// until their disposition is determined. In this latter case, the `prev` dup is moved into
-    /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration.
-    fn update_pending_dups(&mut self) {
-        let prev_bcb = self.prev().bcb;
-        let curr_bcb = self.curr().bcb;
-
-        // Equal coverage spans are ordered by dominators before dominated (if any), so it should be
-        // impossible for `curr` to dominate any previous coverage span.
-        debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb));
-
-        // `prev` is a duplicate of `curr`, so add it to the list of pending dups.
-        // If it dominates `curr`, it will be removed by the subsequent discard step.
-        let prev = self.take_prev().into_dup();
-        debug!(?prev, "adding prev to pending dups");
-        self.pending_dups.push(prev);
-
-        let initial_pending_count = self.pending_dups.len();
-        if initial_pending_count > 0 {
-            self.pending_dups
-                .retain(|dup| !self.basic_coverage_blocks.dominates(dup.bcb, curr_bcb));
-
-            let n_discarded = initial_pending_count - self.pending_dups.len();
-            if n_discarded > 0 {
-                debug!(
-                    "  discarded {n_discarded} of {initial_pending_count} pending_dups that dominated curr",
-                );
-            }
         }
     }
 
@@ -516,19 +374,13 @@ impl<'a> SpansRefiner<'a> {
             if it has statements that end before curr; prev={:?}",
             self.prev()
         );
-        if self.pending_dups.is_empty() {
-            let curr_span = self.curr().span;
-            self.prev_mut().cutoff_statements_at(curr_span.lo());
-            if self.prev().merged_spans.is_empty() {
-                debug!("  ... no non-overlapping statements to add");
-            } else {
-                debug!("  ... adding modified prev={:?}", self.prev());
-                let prev = self.take_prev().into_refined();
-                self.refined_spans.push(prev);
-            }
+
+        let curr_span = self.curr().span;
+        if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) {
+            debug!("after cutoff, adding {prev:?}");
+            self.refined_spans.push(prev);
         } else {
-            // with `pending_dups`, `prev` cannot have any statements that don't overlap
-            self.pending_dups.clear();
+            debug!("prev was eliminated by cutoff");
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 2db358379fe..b91ab811918 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -52,14 +52,19 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
             // - Span A extends further left, or
             // - Both have the same start and span A extends further right
             .then_with(|| Ord::cmp(&a.span.hi(), &b.span.hi()).reverse())
-            // If both spans are equal, sort the BCBs in dominator order,
-            // so that dominating BCBs come before other BCBs they dominate.
-            .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb))
-            // If two spans are otherwise identical, put closure spans first,
-            // as this seems to be what the refinement step expects.
+            // If two spans have the same lo & hi, put closure spans first,
+            // as they take precedence over non-closure spans.
             .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
+            // After deduplication, we want to keep only the most-dominated BCB.
+            .then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
     });
 
+    // Among covspans with the same span, keep only one. Closure spans take
+    // precedence, otherwise keep the one with the most-dominated BCB.
+    // (Ideally we should try to preserve _all_ non-dominating BCBs, but that
+    // requires a lot more complexity in the span refiner, for little benefit.)
+    initial_spans.dedup_by(|b, a| a.span.source_equal(b.span));
+
     initial_spans
 }
 
diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs
index 8575f552f0a..03d952abad1 100644
--- a/compiler/rustc_mir_transform/src/elaborate_drops.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs
@@ -380,7 +380,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
                 LookupResult::Parent(None) => {}
                 LookupResult::Parent(Some(_)) => {
                     if !replace {
-                        self.tcx.dcx().span_delayed_bug(
+                        self.tcx.dcx().span_bug(
                             terminator.source_info.span,
                             format!("drop of untracked value {bb:?}"),
                         );
diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs
index ff4918df9a2..af2d7d4946f 100644
--- a/compiler/rustc_mir_transform/src/errors.rs
+++ b/compiler/rustc_mir_transform/src/errors.rs
@@ -87,6 +87,9 @@ pub(crate) struct RequiresUnsafeDetail {
 }
 
 impl RequiresUnsafeDetail {
+    // FIXME: make this translatable
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     fn add_subdiagnostics<G: EmissionGuarantee>(&self, diag: &mut DiagnosticBuilder<'_, G>) {
         use UnsafetyViolationDetails::*;
         match self.violation {
diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs
index 6f2ef8f9a4f..a9cd688c315 100644
--- a/compiler/rustc_mir_transform/src/const_prop_lint.rs
+++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs
@@ -1,5 +1,8 @@
-//! Propagates constants for early reporting of statically known
-//! assertion failures
+//! A lint that checks for known panics like
+//! overflows, division by zero,
+//! out-of-bound access etc.
+//! Uses const propagation to determine the
+//! values of operands during checks.
 
 use std::fmt::Debug;
 
@@ -21,9 +24,9 @@ use crate::dataflow_const_prop::DummyMachine;
 use crate::errors::{AssertLint, AssertLintKind};
 use crate::MirLint;
 
-pub struct ConstPropLint;
+pub struct KnownPanicsLint;
 
-impl<'tcx> MirLint<'tcx> for ConstPropLint {
+impl<'tcx> MirLint<'tcx> for KnownPanicsLint {
     fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
         if body.tainted_by_errors.is_some() {
             return;
@@ -37,31 +40,28 @@ impl<'tcx> MirLint<'tcx> for ConstPropLint {
         // Only run const prop on functions, methods, closures and associated constants
         if !is_fn_like && !is_assoc_const {
             // skip anon_const/statics/consts because they'll be evaluated by miri anyway
-            trace!("ConstPropLint skipped for {:?}", def_id);
+            trace!("KnownPanicsLint skipped for {:?}", def_id);
             return;
         }
 
         // FIXME(welseywiser) const prop doesn't work on coroutines because of query cycles
         // computing their layout.
         if tcx.is_coroutine(def_id.to_def_id()) {
-            trace!("ConstPropLint skipped for coroutine {:?}", def_id);
+            trace!("KnownPanicsLint skipped for coroutine {:?}", def_id);
             return;
         }
 
-        trace!("ConstPropLint starting for {:?}", def_id);
+        trace!("KnownPanicsLint starting for {:?}", def_id);
 
-        // FIXME(oli-obk, eddyb) Optimize locals (or even local paths) to hold
-        // constants, instead of just checking for const-folding succeeding.
-        // That would require a uniform one-def no-mutation analysis
-        // and RPO (or recursing when needing the value of a local).
         let mut linter = ConstPropagator::new(body, tcx);
         linter.visit_body(body);
 
-        trace!("ConstPropLint done for {:?}", def_id);
+        trace!("KnownPanicsLint done for {:?}", def_id);
     }
 }
 
-/// Finds optimization opportunities on the MIR.
+/// Visits MIR nodes, performs const propagation
+/// and runs lint checks as it goes
 struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, DummyMachine>,
     tcx: TyCtxt<'tcx>,
@@ -238,7 +238,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
                 // dedicated error variants should be introduced instead.
                 assert!(
                     !error.kind().formatted_string(),
-                    "const-prop encountered formatting error: {}",
+                    "known panics lint encountered formatting error: {}",
                     format_interp_error(self.ecx.tcx.dcx(), error),
                 );
                 None
@@ -253,7 +253,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             return None;
         }
 
-        // Normalization needed b/c const prop lint runs in
+        // Normalization needed b/c known panics lint runs in
         // `mir_drops_elaborated_and_const_checked`, which happens before
         // optimized MIR. Only after optimizing the MIR can we guarantee
         // that the `RevealAll` pass has happened and that the body's consts
@@ -864,6 +864,8 @@ pub enum ConstPropMode {
     NoPropagation,
 }
 
+/// A visitor that determines locals in a MIR body
+/// that can be const propagated
 pub struct CanConstProp {
     can_const_prop: IndexVec<Local, ConstPropMode>,
     // False at the beginning. Once set, no more assignments are allowed to that local.
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 74b36eb5ee8..945c3c662a6 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -59,7 +59,6 @@ mod remove_place_mention;
 mod add_subtyping_projections;
 pub mod cleanup_post_borrowck;
 mod const_debuginfo;
-mod const_prop_lint;
 mod copy_prop;
 mod coroutine;
 mod cost_checker;
@@ -83,6 +82,7 @@ mod gvn;
 pub mod inline;
 mod instsimplify;
 mod jump_threading;
+mod known_panics_lint;
 mod large_enums;
 mod lint;
 mod lower_intrinsics;
@@ -533,7 +533,7 @@ fn run_runtime_lowering_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         &elaborate_box_derefs::ElaborateBoxDerefs,
         &coroutine::StateTransform,
         &add_retag::AddRetag,
-        &Lint(const_prop_lint::ConstPropLint),
+        &Lint(known_panics_lint::KnownPanicsLint),
     ];
     pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
 }
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
index bd89874b5cc..e4c30679146 100644
--- a/compiler/rustc_monomorphize/src/errors.rs
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -56,6 +56,7 @@ impl<G: EmissionGuarantee> IntoDiagnostic<'_, G> for UnusedGenericParamsHint {
             // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
             // or a label with a dynamic value in a hard-coded string, but I haven't figured out
             // how to combine the two. 😢
+            #[allow(rustc::untranslatable_diagnostic)]
             diag.span_label(span, format!("generic parameter `{name}` is unused"));
         }
         diag
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index 7c2ecf34c17..55baf6f9f2e 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -27,6 +27,8 @@ parse_async_bound_modifier_in_2015 = `async` trait bounds are only allowed in Ru
 parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
     .label = to use `async fn`, switch to Rust 2018 or later
 
+parse_async_impl = `async` trait implementations are unsupported
+
 parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later
 
 parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 674f7218ea6..2d4447a42c2 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::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
+    codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee,
     IntoDiagnostic, Level, SubdiagnosticMessageOp,
 };
 use rustc_macros::{Diagnostic, Subdiagnostic};
@@ -1475,7 +1475,11 @@ pub(crate) struct FnTraitMissingParen {
 }
 
 impl AddToDiagnostic for FnTraitMissingParen {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
         let applicability = if self.machine_applicable {
             Applicability::MachineApplicable
@@ -2971,3 +2975,10 @@ pub(crate) struct ArrayIndexInOffsetOf(#[primary_span] pub Span);
 #[derive(Diagnostic)]
 #[diag(parse_invalid_offset_of)]
 pub(crate) struct InvalidOffsetOf(#[primary_span] pub Span);
+
+#[derive(Diagnostic)]
+#[diag(parse_async_impl)]
+pub(crate) struct AsyncImpl {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_parse/src/lexer/diagnostics.rs b/compiler/rustc_parse/src/lexer/diagnostics.rs
index b1bd4ac75e5..52a029b20f6 100644
--- a/compiler/rustc_parse/src/lexer/diagnostics.rs
+++ b/compiler/rustc_parse/src/lexer/diagnostics.rs
@@ -1,6 +1,6 @@
 use super::UnmatchedDelim;
 use rustc_ast::token::Delimiter;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_span::source_map::SourceMap;
 use rustc_span::Span;
 
@@ -31,7 +31,7 @@ pub fn same_indentation_level(sm: &SourceMap, open_sp: Span, close_sp: Span) ->
 // When we get a `)` or `]` for `{`, we should emit help message here
 // it's more friendly compared to report `unmatched error` in later phase
 pub fn report_missing_open_delim(
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     unmatched_delims: &[UnmatchedDelim],
 ) -> bool {
     let mut reported_missing_open = false;
@@ -55,7 +55,7 @@ pub fn report_missing_open_delim(
 }
 
 pub fn report_suspicious_mismatch_block(
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     diag_info: &TokenTreeDiagInfo,
     sm: &SourceMap,
     delim: Delimiter,
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 98e062dd784..6545429b95b 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -8,7 +8,7 @@ use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle
 use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Delimiter, Nonterminal};
-use rustc_errors::{codes::*, Diagnostic, PResult};
+use rustc_errors::{codes::*, DiagnosticBuilder, PResult};
 use rustc_span::{sym, BytePos, Span};
 use thin_vec::ThinVec;
 use tracing::debug;
@@ -141,7 +141,7 @@ impl<'a> Parser<'a> {
 
     fn annotate_following_item_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         attr_type: OuterAttributeType,
     ) -> Option<Span> {
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 659716548d9..0cc2170714c 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -34,8 +34,8 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, FatalError, PErr, PResult,
+    pluralize, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, ErrorGuaranteed,
+    FatalError, PErr, PResult,
 };
 use rustc_session::errors::ExprParenthesesNeeded;
 use rustc_span::source_map::Spanned;
@@ -208,11 +208,11 @@ struct MultiSugg {
 }
 
 impl MultiSugg {
-    fn emit(self, err: &mut Diagnostic) {
+    fn emit(self, err: &mut DiagnosticBuilder<'_>) {
         err.multipart_suggestion(self.msg, self.patches, self.applicability);
     }
 
-    fn emit_verbose(self, err: &mut Diagnostic) {
+    fn emit_verbose(self, err: &mut DiagnosticBuilder<'_>) {
         err.multipart_suggestion_verbose(self.msg, self.patches, self.applicability);
     }
 }
@@ -846,7 +846,7 @@ impl<'a> Parser<'a> {
         err.emit();
     }
 
-    fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool {
+    fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool {
         let sm = self.sess.source_map();
         match (&self.prev_token.kind, &self.token.kind) {
             (
@@ -2179,7 +2179,7 @@ impl<'a> Parser<'a> {
 
     pub(super) fn parameter_without_type(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         pat: P<ast::Pat>,
         require_name: bool,
         first_param: bool,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 20b9581f2ef..8826c06bebd 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -25,9 +25,7 @@ use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLim
 use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{
-    AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey,
-};
+use rustc_errors::{AddToDiagnostic, Applicability, DiagnosticBuilder, PResult, StashKey};
 use rustc_lexer::unescape::unescape_char;
 use rustc_macros::Subdiagnostic;
 use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded};
@@ -865,7 +863,7 @@ impl<'a> Parser<'a> {
             );
             let mut err = self.dcx().struct_span_err(span, msg);
 
-            let suggest_parens = |err: &mut Diagnostic| {
+            let suggest_parens = |err: &mut DiagnosticBuilder<'_>| {
                 let suggestions = vec![
                     (span.shrink_to_lo(), "(".to_string()),
                     (span.shrink_to_hi(), ")".to_string()),
@@ -3437,7 +3435,7 @@ impl<'a> Parser<'a> {
         let mut recover_async = false;
         let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD);
 
-        let mut async_block_err = |e: &mut Diagnostic, span: Span| {
+        let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| {
             recover_async = true;
             errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e);
             errors::HelpUseLatestEdition::new().add_to_diagnostic(e);
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index e7b9076bd3c..77381ef4626 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -562,6 +562,15 @@ impl<'a> Parser<'a> {
             self.sess.gated_spans.gate(sym::const_trait_impl, span);
         }
 
+        // Parse stray `impl async Trait`
+        if (self.token.uninterpolated_span().at_least_rust_2018()
+            && self.token.is_keyword(kw::Async))
+            || self.is_kw_followed_by_ident(kw::Async)
+        {
+            self.bump();
+            self.dcx().emit_err(errors::AsyncImpl { span: self.prev_token.span });
+        }
+
         let polarity = self.parse_polarity();
 
         // Parse both types and traits as a type, then reinterpret if necessary.
@@ -592,22 +601,10 @@ impl<'a> Parser<'a> {
             // We need to report this error after `cfg` expansion for compatibility reasons
             self.bump(); // `..`, do not add it to expected tokens
 
-            // FIXME(nnethercote): AST validation later detects this
-            // `TyKind::Err` and emits an errors. So why the unchecked
-            // ErrorGuaranteed?
-            // - A `span_delayed_bug` doesn't work here, because rustfmt can
-            //   hit this path but then not hit the follow-up path in the AST
-            //   validator that issues the error, which results in ICEs.
-            // - `TyKind::Dummy` doesn't work, because it ends up reaching HIR
-            //   lowering, which results in ICEs. Changing `TyKind::Dummy` to
-            //   `TyKind::Err` during AST validation might fix that, but that's
-            //   not possible because AST validation doesn't allow mutability.
-            //
-            // #121072 will hopefully remove all this special handling of the
-            // obsolete `impl Trait for ..` and then this can go away.
-            #[allow(deprecated)]
-            let guar = rustc_errors::ErrorGuaranteed::unchecked_error_guaranteed();
-            Some(self.mk_ty(self.prev_token.span, TyKind::Err(guar)))
+            // AST validation later detects this `TyKind::Dummy` and emits an
+            // error. (#121072 will hopefully remove all this special handling
+            // of the obsolete `impl Trait for ..` and then this can go away.)
+            Some(self.mk_ty(self.prev_token.span, TyKind::Dummy))
         } else if has_for || self.token.can_begin_type() {
             Some(self.parse_ty()?)
         } else {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index f79f2a813b2..23a92e6dd3d 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -778,9 +778,10 @@ impl<'a> Parser<'a> {
             || self.check(&token::Not)
             || self.check(&token::Question)
             || self.check(&token::Tilde)
-            || self.check_keyword(kw::Const)
             || self.check_keyword(kw::For)
             || self.check(&token::OpenDelim(Delimiter::Parenthesis))
+            || self.check_keyword(kw::Const)
+            || self.check_keyword(kw::Async)
     }
 
     /// Parses a bound according to the grammar:
@@ -882,11 +883,13 @@ impl<'a> Parser<'a> {
             BoundConstness::Never
         };
 
-        let asyncness = if self.token.span.at_least_rust_2018() && self.eat_keyword(kw::Async) {
+        let asyncness = if self.token.uninterpolated_span().at_least_rust_2018()
+            && self.eat_keyword(kw::Async)
+        {
             self.sess.gated_spans.gate(sym::async_closure, self.prev_token.span);
             BoundAsyncness::Async(self.prev_token.span)
         } else if self.may_recover()
-            && self.token.span.is_rust_2015()
+            && self.token.uninterpolated_span().is_rust_2015()
             && self.is_kw_followed_by_ident(kw::Async)
         {
             self.bump(); // eat `async`
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 3676eb92a3f..02792491f5e 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -156,9 +156,12 @@ impl<'tcx> CheckConstVisitor<'tcx> {
                 // is a pretty narrow case, however.
                 if tcx.sess.is_nightly_build() {
                     for gate in missing_secondary {
-                        let note =
-                            format!("add `#![feature({gate})]` to the crate attributes to enable",);
-                        err.help(note);
+                        // FIXME: make this translatable
+                        #[allow(rustc::diagnostic_outside_of_impl)]
+                        #[allow(rustc::untranslatable_diagnostic)]
+                        err.help(format!(
+                            "add `#![feature({gate})]` to the crate attributes to enable"
+                        ));
                     }
                 }
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 486396b0677..a3106856a67 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
     ) {
         let variant = match self.typeck_results().node_type(lhs.hir_id).kind() {
             ty::Adt(adt, _) => adt.variant_of_res(res),
-            _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"),
+            _ => {
+                self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern");
+                return;
+            }
         };
         let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len());
         let first_n = pats.iter().enumerate().take(dotdot);
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index a732bdbca51..982def54d30 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -6,9 +6,8 @@ use std::{
 use crate::fluent_generated as fluent;
 use rustc_ast::Label;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    DiagnosticSymbolList, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan,
-    SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticSymbolList,
+    EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp,
 };
 use rustc_hir::{self as hir, ExprKind, Target};
 use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
@@ -1754,7 +1753,11 @@ pub struct UnusedVariableStringInterp {
 }
 
 impl AddToDiagnostic for UnusedVariableStringInterp {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation);
         diag.multipart_suggestion(
             crate::fluent_generated::passes_string_interpolation_only_works,
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index 9a21397789d..d3e3e183845 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -250,7 +250,7 @@ fn get_lang_items(tcx: TyCtxt<'_>, (): ()) -> LanguageItems {
     let mut collector = LanguageItemCollector::new(tcx, resolver);
 
     // Collect lang items in other crates.
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.used_crates(()).iter() {
         for &(def_id, lang_item) in tcx.defined_lang_items(cnum).iter() {
             collector.collect_item(lang_item, def_id, None);
         }
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index 3a8dc377520..487407014d1 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -526,8 +526,8 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
     }
 
     fn define_bindings_in_pat(&mut self, pat: &hir::Pat<'_>, mut succ: LiveNode) -> LiveNode {
-        // In an or-pattern, only consider the first pattern; any later patterns
-        // must have the same bindings, and we also consider the first pattern
+        // In an or-pattern, only consider the first non-never pattern; any later patterns
+        // must have the same bindings, and we also consider that pattern
         // to be the "authoritative" set of ids.
         pat.each_binding_or_first(&mut |_, hir_id, pat_sp, ident| {
             let ln = self.live_node(hir_id, pat_sp);
diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs
index 2dffdc9846c..27619b74a66 100644
--- a/compiler/rustc_pattern_analysis/src/errors.rs
+++ b/compiler/rustc_pattern_analysis/src/errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp};
+use rustc_errors::{AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp};
 use rustc_macros::{LintDiagnostic, Subdiagnostic};
 use rustc_middle::thir::Pat;
 use rustc_middle::ty::Ty;
@@ -62,7 +62,11 @@ pub struct Overlap<'tcx> {
 }
 
 impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _: F,
+    ) {
         let Overlap { span, range } = self;
 
         // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
diff --git a/compiler/rustc_pattern_analysis/src/usefulness.rs b/compiler/rustc_pattern_analysis/src/usefulness.rs
index d35b0248e41..a24da448766 100644
--- a/compiler/rustc_pattern_analysis/src/usefulness.rs
+++ b/compiler/rustc_pattern_analysis/src/usefulness.rs
@@ -760,9 +760,6 @@ impl<'a, Cx: TypeCx> PlaceCtxt<'a, Cx> {
     fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
         self.cx.ctor_arity(ctor, self.ty)
     }
-    fn ctors_for_ty(&self) -> Result<ConstructorSet<Cx>, Cx::Error> {
-        self.cx.ctors_for_ty(self.ty)
-    }
     fn wild_from_ctor(&self, ctor: Constructor<Cx>) -> WitnessPat<Cx> {
         WitnessPat::wild_from_ctor(self.cx, ctor, self.ty.clone())
     }
@@ -815,7 +812,8 @@ impl fmt::Display for ValidityConstraint {
     }
 }
 
-/// Data about a place under investigation.
+/// Data about a place under investigation. Its methods contain a lot of the logic used to analyze
+/// the constructors in the matrix.
 struct PlaceInfo<Cx: TypeCx> {
     /// The type of the place.
     ty: Cx::Ty,
@@ -826,6 +824,8 @@ struct PlaceInfo<Cx: TypeCx> {
 }
 
 impl<Cx: TypeCx> PlaceInfo<Cx> {
+    /// Given a constructor for the current place, we return one `PlaceInfo` for each field of the
+    /// constructor.
     fn specialize<'a>(
         &'a self,
         cx: &'a Cx,
@@ -839,6 +839,77 @@ impl<Cx: TypeCx> PlaceInfo<Cx> {
             is_scrutinee: false,
         })
     }
+
+    /// This analyzes a column of constructors corresponding to the current place. It returns a pair
+    /// `(split_ctors, missing_ctors)`.
+    ///
+    /// `split_ctors` is a splitted list of constructors that cover the whole type. This will be
+    /// used to specialize the matrix.
+    ///
+    /// `missing_ctors` is a list of the constructors not found in the column, for reporting
+    /// purposes.
+    fn split_column_ctors<'a>(
+        &self,
+        cx: &Cx,
+        ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
+    ) -> Result<(SmallVec<[Constructor<Cx>; 1]>, Vec<Constructor<Cx>>), Cx::Error>
+    where
+        Cx: 'a,
+    {
+        let ctors_for_ty = cx.ctors_for_ty(&self.ty)?;
+
+        // We treat match scrutinees of type `!` or `EmptyEnum` differently.
+        let is_toplevel_exception =
+            self.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
+        // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
+        // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
+        let empty_arms_are_unreachable = self.validity.is_known_valid()
+            && (is_toplevel_exception
+                || cx.is_exhaustive_patterns_feature_on()
+                || cx.is_min_exhaustive_patterns_feature_on());
+        // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
+        // toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
+        let can_omit_empty_arms = empty_arms_are_unreachable
+            || is_toplevel_exception
+            || cx.is_exhaustive_patterns_feature_on();
+
+        // Analyze the constructors present in this column.
+        let mut split_set = ctors_for_ty.split(ctors);
+        let all_missing = split_set.present.is_empty();
+
+        // Build the set of constructors we will specialize with. It must cover the whole type, so
+        // we add `Missing` to represent the missing ones. This is explained under "Constructor
+        // Splitting" at the top of this file.
+        let mut split_ctors = split_set.present;
+        if !(split_set.missing.is_empty()
+            && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
+        {
+            split_ctors.push(Constructor::Missing);
+        }
+
+        // Which empty constructors are considered missing. We ensure that
+        // `!missing_ctors.is_empty() => split_ctors.contains(Missing)`. The converse usually holds
+        // except when `!self.validity.is_known_valid()`.
+        let mut missing_ctors = split_set.missing;
+        if !can_omit_empty_arms {
+            missing_ctors.append(&mut split_set.missing_empty);
+        }
+
+        // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top
+        // level we prefer to list all constructors.
+        let report_individual_missing_ctors = self.is_scrutinee || !all_missing;
+        if !missing_ctors.is_empty() && !report_individual_missing_ctors {
+            // Report `_` as missing.
+            missing_ctors = vec![Constructor::Wildcard];
+        } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
+            // We need to report a `_` anyway, so listing other constructors would be redundant.
+            // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
+            // up by diagnostics to add a note about why `_` is required here.
+            missing_ctors = vec![Constructor::NonExhaustive];
+        }
+
+        Ok((split_ctors, missing_ctors))
+    }
 }
 
 impl<Cx: TypeCx> Clone for PlaceInfo<Cx> {
@@ -1314,40 +1385,23 @@ impl<Cx: TypeCx> WitnessMatrix<Cx> {
         pcx: &PlaceCtxt<'_, Cx>,
         missing_ctors: &[Constructor<Cx>],
         ctor: &Constructor<Cx>,
-        report_individual_missing_ctors: bool,
     ) {
         if self.is_empty() {
             return;
         }
         if matches!(ctor, Constructor::Missing) {
             // We got the special `Missing` constructor that stands for the constructors not present
-            // in the match.
-            if missing_ctors.is_empty() {
-                // Nothing to report.
-                *self = Self::empty();
-            } else if !report_individual_missing_ctors {
-                // Report `_` as missing.
-                let pat = pcx.wild_from_ctor(Constructor::Wildcard);
-                self.push_pattern(pat);
-            } else if missing_ctors.iter().any(|c| c.is_non_exhaustive()) {
-                // We need to report a `_` anyway, so listing other constructors would be redundant.
-                // `NonExhaustive` is displayed as `_` just like `Wildcard`, but it will be picked
-                // up by diagnostics to add a note about why `_` is required here.
-                let pat = pcx.wild_from_ctor(Constructor::NonExhaustive);
-                self.push_pattern(pat);
-            } else {
-                // For each missing constructor `c`, we add a `c(_, _, _)` witness appropriately
-                // filled with wildcards.
-                let mut ret = Self::empty();
-                for ctor in missing_ctors {
-                    let pat = pcx.wild_from_ctor(ctor.clone());
-                    // Clone `self` and add `c(_, _, _)` to each of its witnesses.
-                    let mut wit_matrix = self.clone();
-                    wit_matrix.push_pattern(pat);
-                    ret.extend(wit_matrix);
-                }
-                *self = ret;
+            // in the match. For each missing constructor `c`, we add a `c(_, _, _)` witness
+            // appropriately filled with wildcards.
+            let mut ret = Self::empty();
+            for ctor in missing_ctors {
+                let pat = pcx.wild_from_ctor(ctor.clone());
+                // Clone `self` and add `c(_, _, _)` to each of its witnesses.
+                let mut wit_matrix = self.clone();
+                wit_matrix.push_pattern(pat);
+                ret.extend(wit_matrix);
             }
+            *self = ret;
         } else {
             // Any other constructor we unspecialize as expected.
             for witness in self.0.iter_mut() {
@@ -1479,51 +1533,13 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
         };
     };
 
-    let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
-    let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
-    debug!("ty: {:?}", pcx.ty);
-    let ctors_for_ty = pcx.ctors_for_ty()?;
-
-    // We treat match scrutinees of type `!` or `EmptyEnum` differently.
-    let is_toplevel_exception =
-        place.is_scrutinee && matches!(ctors_for_ty, ConstructorSet::NoConstructors);
-    // Whether empty patterns are counted as useful or not. We only warn an empty arm unreachable if
-    // it is guaranteed unreachable by the opsem (i.e. if the place is `known_valid`).
-    let empty_arms_are_unreachable = place.validity.is_known_valid()
-        && (is_toplevel_exception
-            || mcx.tycx.is_exhaustive_patterns_feature_on()
-            || mcx.tycx.is_min_exhaustive_patterns_feature_on());
-    // Whether empty patterns can be omitted for exhaustiveness. We ignore place validity in the
-    // toplevel exception and `exhaustive_patterns` cases for backwards compatibility.
-    let can_omit_empty_arms = empty_arms_are_unreachable
-        || is_toplevel_exception
-        || mcx.tycx.is_exhaustive_patterns_feature_on();
-
     // Analyze the constructors present in this column.
+    debug!("ty: {:?}", place.ty);
     let ctors = matrix.heads().map(|p| p.ctor());
-    let mut split_set = ctors_for_ty.split(ctors);
-    let all_missing = split_set.present.is_empty();
-    // Build the set of constructors we will specialize with. It must cover the whole type.
-    // We need to iterate over a full set of constructors, so we add `Missing` to represent the
-    // missing ones. This is explained under "Constructor Splitting" at the top of this file.
-    let mut split_ctors = split_set.present;
-    if !(split_set.missing.is_empty()
-        && (split_set.missing_empty.is_empty() || empty_arms_are_unreachable))
-    {
-        split_ctors.push(Constructor::Missing);
-    }
-
-    // Whether we should report "Enum::A and Enum::C are missing" or "_ is missing". At the top
-    // level we prefer to list all constructors.
-    let report_individual_missing_ctors = place.is_scrutinee || !all_missing;
-    // Which constructors are considered missing. We ensure that `!missing_ctors.is_empty() =>
-    // split_ctors.contains(Missing)`. The converse usually holds except when
-    // `!place_validity.is_known_valid()`.
-    let mut missing_ctors = split_set.missing;
-    if !can_omit_empty_arms {
-        missing_ctors.append(&mut split_set.missing_empty);
-    }
+    let (split_ctors, missing_ctors) = place.split_column_ctors(mcx.tycx, ctors)?;
 
+    let ty = &place.ty.clone(); // Clone it out so we can mutate `matrix` later.
+    let pcx = &PlaceCtxt { cx: mcx.tycx, ty };
     let mut ret = WitnessMatrix::empty();
     for ctor in split_ctors {
         // Dig into rows that match `ctor`.
@@ -1538,7 +1554,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
         })?;
 
         // Transform witnesses for `spec_matrix` into witnesses for `matrix`.
-        witnesses.apply_constructor(pcx, &missing_ctors, &ctor, report_individual_missing_ctors);
+        witnesses.apply_constructor(pcx, &missing_ctors, &ctor);
         // Accumulate the found witnesses.
         ret.extend(witnesses);
 
diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs
index b6ac54a9ab5..7dc1a1f7917 100644
--- a/compiler/rustc_query_system/src/dep_graph/graph.rs
+++ b/compiler/rustc_query_system/src/dep_graph/graph.rs
@@ -817,7 +817,7 @@ impl<D: Deps> DepGraphData<D> {
             None => {}
         }
 
-        if let None = qcx.dep_context().sess().dcx().has_errors_or_lint_errors_or_delayed_bugs() {
+        if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() {
             panic!("try_mark_previous_green() - Forcing the DepNode should have set its color")
         }
 
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 4b978fefa10..d64a3b43aad 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -7,7 +7,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
     codes::*, pluralize, report_ambiguity_error, struct_span_code_err, Applicability, DiagCtxt,
-    Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle,
+    DiagnosticBuilder, ErrorGuaranteed, MultiSpan, SuggestionStyle,
 };
 use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::def::Namespace::{self, *};
@@ -360,7 +360,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// ```
     fn add_suggestion_for_rename_of_use(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         name: Symbol,
         import: Import<'_>,
         binding_span: Span,
@@ -436,7 +436,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// as characters expected by span manipulations won't be present.
     fn add_suggestion_for_duplicate_nested_use(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         import: Import<'_>,
         binding_span: Span,
     ) {
@@ -1399,7 +1399,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     pub(crate) fn unresolved_macro_suggestions(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         macro_kind: MacroKind,
         parent_scope: &ParentScope<'a>,
         ident: Ident,
@@ -1515,7 +1515,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
 
     pub(crate) fn add_typo_suggestion(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         suggestion: Option<TypoSuggestion>,
         span: Span,
     ) -> bool {
@@ -2461,7 +2461,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
     /// Finds a cfg-ed out item inside `module` with the matching name.
     pub(crate) fn find_cfg_stripped(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         segment: &Symbol,
         module: DefId,
     ) {
@@ -2670,7 +2670,7 @@ pub(crate) enum DiagnosticMode {
 
 pub(crate) fn import_candidates(
     tcx: TyCtxt<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
     candidates: &[ImportSuggestion],
@@ -2696,7 +2696,7 @@ pub(crate) fn import_candidates(
 /// found and suggested, returns `true`, otherwise returns `false`.
 fn show_candidates(
     tcx: TyCtxt<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
     candidates: &[ImportSuggestion],
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 1cf3fecc289..2f4da29133f 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -23,7 +23,6 @@ use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
 use rustc_hir::{PrimTy, TraitCandidate};
-use rustc_metadata::creader::CStore;
 use rustc_middle::middle::resolve_bound_vars::Set1;
 use rustc_middle::{bug, span_bug};
 use rustc_session::config::{CrateType, ResolveDocLinks};
@@ -3683,12 +3682,12 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
             }
             Res::SelfCtor(_) => {
                 // We resolve `Self` in pattern position as an ident sometimes during recovery,
-                // so delay a bug instead of ICEing.
-                self.r.dcx().span_delayed_bug(
+                // so delay a bug instead of ICEing. (Note: is this no longer true? We now ICE. If
+                // this triggers, please convert to a delayed bug and add a test.)
+                self.r.dcx().span_bug(
                     ident.span,
                     "unexpected `SelfCtor` in pattern, expected identifier"
                 );
-                None
             }
             _ => span_bug!(
                 ident.span,
@@ -4574,10 +4573,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
                         // Encoding foreign def ids in proc macro crate metadata will ICE.
                         return None;
                     }
-                    // Doc paths should be resolved speculatively and should not produce any
-                    // diagnostics, but if they are indeed resolved, then we need to keep the
-                    // corresponding crate alive.
-                    CStore::from_tcx_mut(self.r.tcx).set_used_recursively(def_id.krate);
                 }
                 res
             });
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index f71f7ccecab..335bf0949d6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength
+
 use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
 use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
 use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
@@ -16,8 +18,8 @@ use rustc_ast::{
 use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan, SuggestionStyle,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+    MultiSpan, SuggestionStyle,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
@@ -496,7 +498,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn detect_assoc_type_constraint_meant_as_path(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         base_error: &BaseError,
     ) {
         let Some(ty) = self.diagnostic_metadata.current_type_path else {
@@ -537,7 +539,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         }
     }
 
-    fn suggest_self_or_self_ref(&mut self, err: &mut Diagnostic, path: &[Segment], span: Span) {
+    fn suggest_self_or_self_ref(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_>,
+        path: &[Segment],
+        span: Span,
+    ) {
         if !self.self_type_is_available() {
             return;
         }
@@ -582,7 +589,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn try_lookup_name_relaxed(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         following_seg: Option<&Segment>,
@@ -786,7 +793,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_trait_and_bounds(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         res: Option<Res>,
         span: Span,
@@ -863,7 +870,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_typo(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         following_seg: Option<&Segment>,
@@ -903,7 +910,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_shadowed(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         following_seg: Option<&Segment>,
@@ -936,7 +943,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn err_code_special_cases(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         span: Span,
@@ -981,7 +988,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     /// Emit special messages for unresolved `Self` and `self`.
     fn suggest_self_ty(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         span: Span,
@@ -1008,7 +1015,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_self_value(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         path: &[Segment],
         span: Span,
@@ -1090,7 +1097,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_at_operator_in_slice_pat_with_range(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         path: &[Segment],
     ) {
         let Some(pat) = self.diagnostic_metadata.current_pat else { return };
@@ -1129,7 +1136,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_swapping_misplaced_self_ty_and_trait(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         res: Option<Res>,
         span: Span,
@@ -1155,7 +1162,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         }
     }
 
-    fn suggest_bare_struct_literal(&mut self, err: &mut Diagnostic) {
+    fn suggest_bare_struct_literal(&mut self, err: &mut DiagnosticBuilder<'_>) {
         if let Some(span) = self.diagnostic_metadata.current_block_could_be_bare_struct_literal {
             err.multipart_suggestion(
                 "you might have meant to write a `struct` literal",
@@ -1170,7 +1177,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_changing_type_to_const_param(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         res: Option<Res>,
         source: PathSource<'_>,
         span: Span,
@@ -1222,7 +1229,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_pattern_match_with_let(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         span: Span,
     ) -> bool {
@@ -1277,7 +1284,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     }
 
     /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
-    fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnostic) -> bool {
+    fn restrict_assoc_type_in_where_clause(
+        &mut self,
+        span: Span,
+        err: &mut DiagnosticBuilder<'_>,
+    ) -> bool {
         // Detect that we are actually in a `where` predicate.
         let (bounded_ty, bounds, where_span) =
             if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
@@ -1410,7 +1421,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     /// Returns `true` if able to provide context-dependent help.
     fn smart_resolve_context_dependent_help(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         source: PathSource<'_>,
         path: &[Segment],
@@ -1421,50 +1432,52 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         let ns = source.namespace();
         let is_expected = &|res| source.is_expected(res);
 
-        let path_sep = |this: &mut Self, err: &mut Diagnostic, expr: &Expr, kind: DefKind| {
-            const MESSAGE: &str = "use the path separator to refer to an item";
+        let path_sep =
+            |this: &mut Self, err: &mut DiagnosticBuilder<'_>, expr: &Expr, kind: DefKind| {
+                const MESSAGE: &str = "use the path separator to refer to an item";
 
-            let (lhs_span, rhs_span) = match &expr.kind {
-                ExprKind::Field(base, ident) => (base.span, ident.span),
-                ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => {
-                    (receiver.span, *span)
-                }
-                _ => return false,
-            };
+                let (lhs_span, rhs_span) = match &expr.kind {
+                    ExprKind::Field(base, ident) => (base.span, ident.span),
+                    ExprKind::MethodCall(box MethodCall { receiver, span, .. }) => {
+                        (receiver.span, *span)
+                    }
+                    _ => return false,
+                };
 
-            if lhs_span.eq_ctxt(rhs_span) {
-                err.span_suggestion(
-                    lhs_span.between(rhs_span),
-                    MESSAGE,
-                    "::",
-                    Applicability::MaybeIncorrect,
-                );
-                true
-            } else if kind == DefKind::Struct
-                && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span)
-                && let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
-            {
-                // The LHS is a type that originates from a macro call.
-                // We have to add angle brackets around it.
+                if lhs_span.eq_ctxt(rhs_span) {
+                    err.span_suggestion(
+                        lhs_span.between(rhs_span),
+                        MESSAGE,
+                        "::",
+                        Applicability::MaybeIncorrect,
+                    );
+                    true
+                } else if kind == DefKind::Struct
+                    && let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span)
+                    && let Ok(snippet) =
+                        this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
+                {
+                    // The LHS is a type that originates from a macro call.
+                    // We have to add angle brackets around it.
 
-                err.span_suggestion_verbose(
-                    lhs_source_span.until(rhs_span),
-                    MESSAGE,
-                    format!("<{snippet}>::"),
-                    Applicability::MaybeIncorrect,
-                );
-                true
-            } else {
-                // Either we were unable to obtain the source span / the snippet or
-                // the LHS originates from a macro call and it is not a type and thus
-                // there is no way to replace `.` with `::` and still somehow suggest
-                // valid Rust code.
+                    err.span_suggestion_verbose(
+                        lhs_source_span.until(rhs_span),
+                        MESSAGE,
+                        format!("<{snippet}>::"),
+                        Applicability::MaybeIncorrect,
+                    );
+                    true
+                } else {
+                    // Either we were unable to obtain the source span / the snippet or
+                    // the LHS originates from a macro call and it is not a type and thus
+                    // there is no way to replace `.` with `::` and still somehow suggest
+                    // valid Rust code.
 
-                false
-            }
-        };
+                    false
+                }
+            };
 
-        let find_span = |source: &PathSource<'_>, err: &mut Diagnostic| {
+        let find_span = |source: &PathSource<'_>, err: &mut DiagnosticBuilder<'_>| {
             match source {
                 PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. }))
                 | PathSource::TupleStruct(span, _) => {
@@ -1820,7 +1833,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     fn suggest_alternative_construction_methods(
         &mut self,
         def_id: DefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         path_span: Span,
         call_span: Span,
         args: &[P<Expr>],
@@ -2250,7 +2263,11 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     // try to give a suggestion for this pattern: `name = blah`, which is common in other languages
     // suggest `let name = blah` to introduce a new binding
-    fn let_binding_suggestion(&mut self, err: &mut Diagnostic, ident_span: Span) -> bool {
+    fn let_binding_suggestion(
+        &mut self,
+        err: &mut DiagnosticBuilder<'_>,
+        ident_span: Span,
+    ) -> bool {
         if let Some(Expr { kind: ExprKind::Assign(lhs, ..), .. }) =
             self.diagnostic_metadata.in_assignment
             && let ast::ExprKind::Path(None, ref path) = lhs.kind
@@ -2351,7 +2368,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
     /// Adds a suggestion for using an enum's variant when an enum is used instead.
     fn suggest_using_enum_variant(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         source: PathSource<'_>,
         def_id: DefId,
         span: Span,
@@ -2727,9 +2744,9 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn suggest_introducing_lifetime(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         name: Option<&str>,
-        suggest: impl Fn(&mut Diagnostic, bool, Span, Cow<'static, str>, String) -> bool,
+        suggest: impl Fn(&mut DiagnosticBuilder<'_>, bool, Span, Cow<'static, str>, String) -> bool,
     ) {
         let mut suggest_note = true;
         for rib in self.lifetime_ribs.iter().rev() {
@@ -2887,7 +2904,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
 
     fn add_missing_lifetime_specifiers_label(
         &mut self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         lifetime_refs: Vec<MissingLifetime>,
         function_param_lifetimes: Option<(Vec<MissingLifetime>, Vec<ElisionFnParameter>)>,
     ) {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 1c625f49e3e..a9b6703f3ab 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1651,7 +1651,6 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
             self.tcx
                 .sess
                 .time("resolve_postprocess", || self.crate_loader(|c| c.postprocess(krate)));
-            self.crate_loader(|c| c.unload_unused_crates());
         });
 
         // Make sure we don't mutate the cstore from here on.
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index ef0512bf686..0a330da87b0 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2468,6 +2468,9 @@ pub fn parse_externs(
             ));
             let adjusted_name = name.replace('-', "_");
             if is_ascii_ident(&adjusted_name) {
+                // FIXME: make this translatable
+                #[allow(rustc::diagnostic_outside_of_impl)]
+                #[allow(rustc::untranslatable_diagnostic)]
                 error.help(format!(
                     "consider replacing the dashes with underscores: `{adjusted_name}`"
                 ));
diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs
index db976b30404..74d26237f24 100644
--- a/compiler/rustc_session/src/output.rs
+++ b/compiler/rustc_session/src/output.rs
@@ -6,6 +6,7 @@ use crate::errors::{
 };
 use crate::Session;
 use rustc_ast::{self as ast, attr};
+use rustc_errors::FatalError;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
 use std::path::Path;
@@ -115,7 +116,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option<Span>) {
     }
 
     if err_count > 0 {
-        sess.dcx().abort_if_errors();
+        FatalError.raise();
     }
 }
 
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 701c5bf375f..b011ca4dd50 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -15,7 +15,8 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_data_structures::sync::{AppendOnlyVec, Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, DiagCtxt};
 use rustc_errors::{
-    fallback_fluent_bundle, Diagnostic, DiagnosticBuilder, DiagnosticMessage, MultiSpan, StashKey,
+    fallback_fluent_bundle, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, MultiSpan,
+    StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
@@ -156,7 +157,11 @@ pub fn feature_warn_issue(
 }
 
 /// Adds the diagnostics for a feature to an existing error.
-pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &Session, feature: Symbol) {
+pub fn add_feature_diagnostics<G: EmissionGuarantee>(
+    err: &mut DiagnosticBuilder<'_, G>,
+    sess: &Session,
+    feature: Symbol,
+) {
     add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language, false);
 }
 
@@ -165,8 +170,8 @@ pub fn add_feature_diagnostics(err: &mut Diagnostic, sess: &Session, feature: Sy
 /// This variant allows you to control whether it is a library or language feature.
 /// Almost always, you want to use this for a language feature. If so, prefer
 /// `add_feature_diagnostics`.
-pub fn add_feature_diagnostics_for_issue(
-    err: &mut Diagnostic,
+pub fn add_feature_diagnostics_for_issue<G: EmissionGuarantee>(
+    err: &mut DiagnosticBuilder<'_, G>,
     sess: &Session,
     feature: Symbol,
     issue: GateIssue,
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 9d1133c487f..02c7a0c6371 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -258,7 +258,8 @@ impl Session {
         }
     }
 
-    fn check_miri_unleashed_features(&self) {
+    fn check_miri_unleashed_features(&self) -> Option<ErrorGuaranteed> {
+        let mut guar = None;
         let unleashed_features = self.miri_unleashed_features.lock();
         if !unleashed_features.is_empty() {
             let mut must_err = false;
@@ -279,18 +280,22 @@ impl Session {
             // If we should err, make sure we did.
             if must_err && self.dcx().has_errors().is_none() {
                 // We have skipped a feature gate, and not run into other errors... reject.
-                self.dcx().emit_err(errors::NotCircumventFeature);
+                guar = Some(self.dcx().emit_err(errors::NotCircumventFeature));
             }
         }
+        guar
     }
 
     /// Invoked all the way at the end to finish off diagnostics printing.
-    pub fn finish_diagnostics(&self, registry: &Registry) {
-        self.check_miri_unleashed_features();
+    pub fn finish_diagnostics(&self, registry: &Registry) -> Option<ErrorGuaranteed> {
+        let mut guar = None;
+        guar = guar.or(self.check_miri_unleashed_features());
+        guar = guar.or(self.dcx().emit_stashed_diagnostics());
         self.dcx().print_error_count(registry);
         if self.opts.json_future_incompat {
             self.dcx().emit_future_breakage_report();
         }
+        guar
     }
 
     /// Returns true if the crate is a testing one.
@@ -312,16 +317,6 @@ impl Session {
         err
     }
 
-    pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> {
-        // We must include lint errors here.
-        if let Some(reported) = self.dcx().has_errors_or_lint_errors() {
-            self.dcx().emit_stashed_diagnostics();
-            Err(reported)
-        } else {
-            Ok(())
-        }
-    }
-
     /// Record the fact that we called `trimmed_def_paths`, and do some
     /// checking about whether its cost was justified.
     pub fn record_trimmed_def_paths(&self) {
@@ -1410,10 +1405,6 @@ impl EarlyDiagCtxt {
         Self { dcx: DiagCtxt::with_emitter(emitter) }
     }
 
-    pub fn abort_if_errors(&self) {
-        self.dcx.abort_if_errors()
-    }
-
     /// Swap out the underlying dcx once we acquire the user's preference on error emission
     /// format. Any errors prior to that will cause an abort and all stashed diagnostics of the
     /// previous dcx will be emitted.
diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs
index 6bb8c5452b9..6e870728baf 100644
--- a/compiler/rustc_smir/src/rustc_internal/mod.rs
+++ b/compiler/rustc_smir/src/rustc_internal/mod.rs
@@ -347,8 +347,14 @@ macro_rules! run_driver {
                         Err(CompilerError::Interrupted(value))
                     }
                     (Ok(Ok(_)), None) => Err(CompilerError::Skipped),
-                    (Ok(Err(_)), _) => Err(CompilerError::CompilationFailed),
-                    (Err(_), _) => Err(CompilerError::ICE),
+                    // Two cases here:
+                    // - `run` finished normally and returned `Err`
+                    // - `run` panicked with `FatalErr`
+                    // You might think that normal compile errors cause the former, and
+                    // ICEs cause the latter. But some normal compiler errors also cause
+                    // the latter. So we can't meaningfully distinguish them, and group
+                    // them together.
+                    (Ok(Err(_)), _) | (Err(_), _) => Err(CompilerError::Failed),
                 }
             }
         }
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 29c88783357..181ab0d4d56 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -764,8 +764,10 @@ symbols! {
         f64_nan,
         fabsf32,
         fabsf64,
+        fadd_algebraic,
         fadd_fast,
         fake_variadic,
+        fdiv_algebraic,
         fdiv_fast,
         feature,
         fence,
@@ -785,6 +787,7 @@ symbols! {
         fmaf32,
         fmaf64,
         fmt,
+        fmul_algebraic,
         fmul_fast,
         fn_align,
         fn_delegation,
@@ -810,6 +813,7 @@ symbols! {
         format_unsafe_arg,
         freeze,
         freg,
+        frem_algebraic,
         frem_fast,
         from,
         from_desugaring,
@@ -823,6 +827,7 @@ symbols! {
         from_usize,
         from_yeet,
         fs_create_dir,
+        fsub_algebraic,
         fsub_fast,
         fundamental,
         future,
diff --git a/compiler/rustc_target/src/spec/base/apple/mod.rs b/compiler/rustc_target/src/spec/base/apple/mod.rs
index aee5d60626e..dd75377ead2 100644
--- a/compiler/rustc_target/src/spec/base/apple/mod.rs
+++ b/compiler/rustc_target/src/spec/base/apple/mod.rs
@@ -97,28 +97,39 @@ fn pre_link_args(os: &'static str, arch: Arch, abi: &'static str) -> LinkArgs {
         _ => os.into(),
     };
 
-    let platform_version: StaticCow<str> = match os {
-        "ios" => ios_lld_platform_version(arch),
-        "tvos" => tvos_lld_platform_version(),
-        "watchos" => watchos_lld_platform_version(),
-        "macos" => macos_lld_platform_version(arch),
-        _ => unreachable!(),
-    }
-    .into();
-
-    let arch = arch.target_name();
+    let min_version: StaticCow<str> = {
+        let (major, minor) = match os {
+            "ios" => ios_deployment_target(arch, abi),
+            "tvos" => tvos_deployment_target(),
+            "watchos" => watchos_deployment_target(),
+            "macos" => macos_deployment_target(arch),
+            _ => unreachable!(),
+        };
+        format!("{major}.{minor}").into()
+    };
+    let sdk_version = min_version.clone();
 
     let mut args = TargetOptions::link_args(
         LinkerFlavor::Darwin(Cc::No, Lld::No),
-        &["-arch", arch, "-platform_version"],
+        &["-arch", arch.target_name(), "-platform_version"],
     );
     add_link_args_iter(
         &mut args,
         LinkerFlavor::Darwin(Cc::No, Lld::No),
-        [platform_name, platform_version.clone(), platform_version].into_iter(),
+        [platform_name, min_version, sdk_version].into_iter(),
     );
     if abi != "macabi" {
-        add_link_args(&mut args, LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-arch", arch]);
+        add_link_args(
+            &mut args,
+            LinkerFlavor::Darwin(Cc::Yes, Lld::No),
+            &["-arch", arch.target_name()],
+        );
+    } else {
+        add_link_args_iter(
+            &mut args,
+            LinkerFlavor::Darwin(Cc::Yes, Lld::No),
+            ["-target".into(), mac_catalyst_llvm_target(arch).into()].into_iter(),
+        );
     }
 
     args
@@ -131,7 +142,7 @@ pub fn opts(os: &'static str, arch: Arch) -> TargetOptions {
         abi: abi.into(),
         os: os.into(),
         cpu: arch.target_cpu().into(),
-        link_env_remove: link_env_remove(arch, os),
+        link_env_remove: link_env_remove(os),
         vendor: "apple".into(),
         linker_flavor: LinkerFlavor::Darwin(Cc::Yes, Lld::No),
         // macOS has -dead_strip, which doesn't rely on function_sections
@@ -220,16 +231,13 @@ pub fn deployment_target(target: &Target) -> Option<(u32, u32)> {
             };
             macos_deployment_target(arch)
         }
-        "ios" => match &*target.abi {
-            "macabi" => mac_catalyst_deployment_target(),
-            _ => {
-                let arch = match target.arch.as_ref() {
-                    "arm64e" => Arm64e,
-                    _ => Arm64,
-                };
-                ios_deployment_target(arch)
-            }
-        },
+        "ios" => {
+            let arch = match target.arch.as_ref() {
+                "arm64e" => Arm64e,
+                _ => Arm64,
+            };
+            ios_deployment_target(arch, &target.abi)
+        }
         "watchos" => watchos_deployment_target(),
         "tvos" => tvos_deployment_target(),
         _ => return None,
@@ -260,17 +268,12 @@ fn macos_deployment_target(arch: Arch) -> (u32, u32) {
         .unwrap_or_else(|| macos_default_deployment_target(arch))
 }
 
-fn macos_lld_platform_version(arch: Arch) -> String {
-    let (major, minor) = macos_deployment_target(arch);
-    format!("{major}.{minor}")
-}
-
 pub fn macos_llvm_target(arch: Arch) -> String {
     let (major, minor) = macos_deployment_target(arch);
     format!("{}-apple-macosx{}.{}.0", arch.target_name(), major, minor)
 }
 
-fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]> {
+fn link_env_remove(os: &'static str) -> StaticCow<[StaticCow<str>]> {
     // Apple platforms only officially support macOS as a host for any compilation.
     //
     // If building for macOS, we go ahead and remove any erroneous environment state
@@ -298,29 +301,23 @@ fn link_env_remove(arch: Arch, os: &'static str) -> StaticCow<[StaticCow<str>]>
         env_remove.push("TVOS_DEPLOYMENT_TARGET".into());
         env_remove.into()
     } else {
-        // Otherwise if cross-compiling for a different OS/SDK, remove any part
+        // Otherwise if cross-compiling for a different OS/SDK (including Mac Catalyst), remove any part
         // of the linking environment that's wrong and reversed.
-        match arch {
-            Armv7k | Armv7s | Arm64 | Arm64e | Arm64_32 | I386 | I386_sim | I686 | X86_64
-            | X86_64_sim | X86_64h | Arm64_sim => {
-                cvs!["MACOSX_DEPLOYMENT_TARGET"]
-            }
-            X86_64_macabi | Arm64_macabi => cvs!["IPHONEOS_DEPLOYMENT_TARGET"],
-        }
+        cvs!["MACOSX_DEPLOYMENT_TARGET"]
     }
 }
 
-fn ios_deployment_target(arch: Arch) -> (u32, u32) {
+fn ios_deployment_target(arch: Arch, abi: &str) -> (u32, u32) {
     // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
-    let (major, minor) = if arch == Arm64e { (14, 0) } else { (10, 0) };
+    let (major, minor) = match (arch, abi) {
+        (Arm64e, _) => (14, 0),
+        // Mac Catalyst defaults to 13.1 in Clang.
+        (_, "macabi") => (13, 1),
+        _ => (10, 0),
+    };
     from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((major, minor))
 }
 
-fn mac_catalyst_deployment_target() -> (u32, u32) {
-    // If you are looking for the default deployment target, prefer `rustc --print deployment-target`.
-    from_set_deployment_target("IPHONEOS_DEPLOYMENT_TARGET").unwrap_or((14, 0))
-}
-
 pub fn ios_llvm_target(arch: Arch) -> String {
     // Modern iOS tooling extracts information about deployment target
     // from LC_BUILD_VERSION. This load command will only be emitted when
@@ -328,17 +325,17 @@ pub fn ios_llvm_target(arch: Arch) -> String {
     // set high enough. Luckily one LC_BUILD_VERSION is enough, for Xcode
     // to pick it up (since std and core are still built with the fallback
     // of version 7.0 and hence emit the old LC_IPHONE_MIN_VERSION).
-    let (major, minor) = ios_deployment_target(arch);
+    let (major, minor) = ios_deployment_target(arch, "");
     format!("{}-apple-ios{}.{}.0", arch.target_name(), major, minor)
 }
 
-fn ios_lld_platform_version(arch: Arch) -> String {
-    let (major, minor) = ios_deployment_target(arch);
-    format!("{major}.{minor}")
+pub fn mac_catalyst_llvm_target(arch: Arch) -> String {
+    let (major, minor) = ios_deployment_target(arch, "macabi");
+    format!("{}-apple-ios{}.{}.0-macabi", arch.target_name(), major, minor)
 }
 
 pub fn ios_sim_llvm_target(arch: Arch) -> String {
-    let (major, minor) = ios_deployment_target(arch);
+    let (major, minor) = ios_deployment_target(arch, "sim");
     format!("{}-apple-ios{}.{}.0-simulator", arch.target_name(), major, minor)
 }
 
@@ -347,11 +344,6 @@ fn tvos_deployment_target() -> (u32, u32) {
     from_set_deployment_target("TVOS_DEPLOYMENT_TARGET").unwrap_or((10, 0))
 }
 
-fn tvos_lld_platform_version() -> String {
-    let (major, minor) = tvos_deployment_target();
-    format!("{major}.{minor}")
-}
-
 pub fn tvos_llvm_target(arch: Arch) -> String {
     let (major, minor) = tvos_deployment_target();
     format!("{}-apple-tvos{}.{}.0", arch.target_name(), major, minor)
@@ -367,11 +359,6 @@ fn watchos_deployment_target() -> (u32, u32) {
     from_set_deployment_target("WATCHOS_DEPLOYMENT_TARGET").unwrap_or((5, 0))
 }
 
-fn watchos_lld_platform_version() -> String {
-    let (major, minor) = watchos_deployment_target();
-    format!("{major}.{minor}")
-}
-
 pub fn watchos_sim_llvm_target(arch: Arch) -> String {
     let (major, minor) = watchos_deployment_target();
     format!("{}-apple-watchos{}.{}.0-simulator", arch.target_name(), major, minor)
diff --git a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
index 78067a138a9..300e3014079 100644
--- a/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/targets/aarch64_apple_ios_macabi.rs
@@ -1,16 +1,13 @@
-use crate::spec::base::apple::{opts, Arch};
-use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions};
+use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch};
+use crate::spec::{FramePointer, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let llvm_target = "arm64-apple-ios14.0-macabi";
-
     let arch = Arch::Arm64_macabi;
     let mut base = opts("ios", arch);
-    base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
 
     Target {
-        llvm_target: llvm_target.into(),
+        llvm_target: mac_catalyst_llvm_target(arch).into(),
         pointer_width: 64,
         data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
         arch: arch.target_arch(),
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
index f4350708986..234270c999b 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_gnu.rs
@@ -1,4 +1,4 @@
-use crate::spec::{base, CodeModel, Target, TargetOptions};
+use crate::spec::{base, Target, TargetOptions};
 
 pub fn target() -> Target {
     Target {
@@ -7,7 +7,6 @@ pub fn target() -> Target {
         data_layout: "e-m:e-p:64:64-i64:64-i128:128-n64-S128".into(),
         arch: "loongarch64".into(),
         options: TargetOptions {
-            code_model: Some(CodeModel::Medium),
             cpu: "generic".into(),
             features: "+f,+d".into(),
             llvm_abiname: "lp64d".into(),
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
index f448017a2a5..3b1ea8e206f 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none.rs
@@ -16,7 +16,7 @@ pub fn target() -> Target {
             max_atomic_width: Some(64),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
-            code_model: Some(CodeModel::Medium),
+            code_model: Some(CodeModel::Small),
             ..Default::default()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
index d636c9599a7..ab9300ef9c7 100644
--- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
+++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_none_softfloat.rs
@@ -17,7 +17,7 @@ pub fn target() -> Target {
             max_atomic_width: Some(64),
             relocation_model: RelocModel::Static,
             panic_strategy: PanicStrategy::Abort,
-            code_model: Some(CodeModel::Medium),
+            code_model: Some(CodeModel::Small),
             ..Default::default()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
index ff21e489333..e59f41185de 100644
--- a/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
+++ b/compiler/rustc_target/src/spec/targets/x86_64_apple_ios_macabi.rs
@@ -1,16 +1,13 @@
-use crate::spec::base::apple::{opts, Arch};
-use crate::spec::{Cc, LinkerFlavor, Lld, SanitizerSet, Target, TargetOptions};
+use crate::spec::base::apple::{mac_catalyst_llvm_target, opts, Arch};
+use crate::spec::{SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let llvm_target = "x86_64-apple-ios14.0-macabi";
-
     let arch = Arch::X86_64_macabi;
     let mut base = opts("ios", arch);
-    base.add_pre_link_args(LinkerFlavor::Darwin(Cc::Yes, Lld::No), &["-target", llvm_target]);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::LEAK | SanitizerSet::THREAD;
 
     Target {
-        llvm_target: llvm_target.into(),
+        llvm_target: mac_catalyst_llvm_target(arch).into(),
         pointer_width: 64,
         data_layout:
             "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128".into(),
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 407fff03e15..10911c0d002 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1,7 +1,7 @@
 use crate::fluent_generated as fluent;
 use rustc_errors::{
-    codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder,
-    EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp,
+    codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee,
+    IntoDiagnostic, Level, SubdiagnosticMessageOp,
 };
 use rustc_macros::Diagnostic;
 use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty};
@@ -102,7 +102,11 @@ pub enum AdjustSignatureBorrow {
 }
 
 impl AddToDiagnostic for AdjustSignatureBorrow {
-    fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
+    fn add_to_diagnostic_with<G: EmissionGuarantee, F: SubdiagnosticMessageOp<G>>(
+        self,
+        diag: &mut DiagnosticBuilder<'_, G>,
+        _f: F,
+    ) {
         match self {
             AdjustSignatureBorrow::Borrow { to_borrow } => {
                 diag.arg("len", to_borrow.len());
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 63555a305d8..97f715b6386 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -109,7 +109,10 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
         let mut errors = Vec::new();
         for i in 0.. {
             if !infcx.tcx.recursion_limit().value_within_limit(i) {
-                unimplemented!("overflowed on pending obligations: {:?}", self.obligations);
+                // Only return true errors that we have accumulated while processing;
+                // keep ambiguities around, *including overflows*, because they shouldn't
+                // be considered true errors.
+                return errors;
             }
 
             let mut has_changed = false;
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index d87cc89954a..91312c9fdd6 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,6 +1,6 @@
 use crate::traits::error_reporting::TypeErrCtxtExt;
 use crate::traits::query::evaluate_obligation::InferCtxtExt;
-use crate::traits::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::at::At;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -85,25 +85,16 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
             ),
         );
 
-        // Do not emit an error if normalization is known to fail but instead
-        // keep the projection unnormalized. This is the case for projections
-        // with a `T: Trait` where-clause and opaque types outside of the defining
-        // scope.
-        let result = if infcx.predicate_may_hold(&obligation) {
-            self.fulfill_cx.register_predicate_obligation(infcx, obligation);
-            let errors = self.fulfill_cx.select_all_or_error(infcx);
-            if !errors.is_empty() {
-                return Err(errors);
-            }
-            let ty = infcx.resolve_vars_if_possible(new_infer_ty);
-
-            // Alias is guaranteed to be fully structurally resolved,
-            // so we can super fold here.
-            ty.try_super_fold_with(self)?
-        } else {
-            alias_ty.try_super_fold_with(self)?
-        };
+        self.fulfill_cx.register_predicate_obligation(infcx, obligation);
+        let errors = self.fulfill_cx.select_all_or_error(infcx);
+        if !errors.is_empty() {
+            return Err(errors);
+        }
 
+        // Alias is guaranteed to be fully structurally resolved,
+        // so we can super fold here.
+        let ty = infcx.resolve_vars_if_possible(new_infer_ty);
+        let result = ty.try_super_fold_with(self)?;
         self.depth -= 1;
         Ok(result)
     }
@@ -178,6 +169,7 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
         Ok(t)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
         let infcx = self.at.infcx;
         debug_assert_eq!(ty, infcx.shallow_resolve(ty));
@@ -204,11 +196,11 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx> {
         }
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> {
-        let reveal = self.at.param_env.reveal();
         let infcx = self.at.infcx;
         debug_assert_eq!(ct, infcx.shallow_resolve(ct));
-        if !needs_normalization(&ct, reveal) {
+        if !ct.has_projections() {
             return Ok(ct);
         }
 
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
new file mode 100644
index 00000000000..911462f4b9a
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/anon_const.rs
@@ -0,0 +1,25 @@
+use crate::solve::EvalCtxt;
+use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
+use rustc_middle::ty;
+
+impl<'tcx> EvalCtxt<'_, 'tcx> {
+    #[instrument(level = "debug", skip(self), ret)]
+    pub(super) fn normalize_anon_const(
+        &mut self,
+        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
+    ) -> QueryResult<'tcx> {
+        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),
+            self.tcx()
+                .type_of(goal.predicate.alias.def_id)
+                .no_bound_vars()
+                .expect("const ty should not rely on other generics"),
+        ) {
+            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+        } else {
+            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index d177109c420..5f625831156 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -18,8 +18,9 @@ use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::ty::{ToPredicate, TypeVisitableExt};
 use rustc_span::{sym, ErrorGuaranteed, DUMMY_SP};
 
+mod anon_const;
 mod inherent;
-mod opaques;
+mod opaque_types;
 mod weak_types;
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -31,34 +32,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let def_id = goal.predicate.def_id();
         match self.tcx().def_kind(def_id) {
             DefKind::AssocTy | DefKind::AssocConst => {
-                // To only compute normalization once for each projection we only
-                // assemble normalization candidates if the expected term is an
-                // unconstrained inference variable.
-                //
-                // Why: For better cache hits, since if we have an unconstrained RHS then
-                // there are only as many cache keys as there are (canonicalized) alias
-                // types in each normalizes-to goal. This also weakens inference in a
-                // forwards-compatible way so we don't use the value of the RHS term to
-                // affect candidate assembly for projections.
-                //
-                // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
-                // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
-                // `U` and equate it with `u32`. This means that we don't need a separate
-                // projection cache in the solver, since we're piggybacking off of regular
-                // goal caching.
-                if self.term_is_fully_unconstrained(goal) {
-                    match self.tcx().associated_item(def_id).container {
-                        ty::AssocItemContainer::TraitContainer => {
+                match self.tcx().associated_item(def_id).container {
+                    ty::AssocItemContainer::TraitContainer => {
+                        // To only compute normalization once for each projection we only
+                        // assemble normalization candidates if the expected term is an
+                        // unconstrained inference variable.
+                        //
+                        // Why: For better cache hits, since if we have an unconstrained RHS then
+                        // there are only as many cache keys as there are (canonicalized) alias
+                        // types in each normalizes-to goal. This also weakens inference in a
+                        // forwards-compatible way so we don't use the value of the RHS term to
+                        // affect candidate assembly for projections.
+                        //
+                        // E.g. for `<T as Trait>::Assoc == u32` we recursively compute the goal
+                        // `exists<U> <T as Trait>::Assoc == U` and then take the resulting type for
+                        // `U` and equate it with `u32`. This means that we don't need a separate
+                        // projection cache in the solver, since we're piggybacking off of regular
+                        // goal caching.
+                        if self.term_is_fully_unconstrained(goal) {
                             let candidates = self.assemble_and_evaluate_candidates(goal);
                             self.merge_candidates(candidates)
+                        } else {
+                            self.set_normalizes_to_hack_goal(goal);
+                            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
                         }
-                        ty::AssocItemContainer::ImplContainer => {
-                            self.normalize_inherent_associated_type(goal)
-                        }
                     }
-                } else {
-                    self.set_normalizes_to_hack_goal(goal);
-                    self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
+                    ty::AssocItemContainer::ImplContainer => {
+                        self.normalize_inherent_associated_type(goal)
+                    }
                 }
             }
             DefKind::AnonConst => self.normalize_anon_const(goal),
@@ -67,26 +68,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             kind => bug!("unknown DefKind {} in projection goal: {goal:#?}", kind.descr(def_id)),
         }
     }
-
-    #[instrument(level = "debug", skip(self), ret)]
-    fn normalize_anon_const(
-        &mut self,
-        goal: Goal<'tcx, ty::NormalizesTo<'tcx>>,
-    ) -> QueryResult<'tcx> {
-        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),
-            self.tcx()
-                .type_of(goal.predicate.alias.def_id)
-                .no_bound_vars()
-                .expect("const ty should not rely on other generics"),
-        ) {
-            self.eq(goal.param_env, normalized_const, goal.predicate.term.ct().unwrap())?;
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        } else {
-            self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
-        }
-    }
 }
 
 impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
index 356c3776c04..356c3776c04 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaque_types.rs
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index f663f02f872..3619d02438d 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -18,7 +18,7 @@ use crate::traits::{
     Obligation, ObligationCause, PredicateObligation, PredicateObligations, SelectionContext,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_infer::infer::{DefineOpaqueTypes, InferCtxt, TyCtxtInferExt};
@@ -58,7 +58,7 @@ pub struct OverlapResult<'tcx> {
     pub involves_placeholder: bool,
 }
 
-pub fn add_placeholder_note(err: &mut Diagnostic) {
+pub fn add_placeholder_note<G: EmissionGuarantee>(err: &mut DiagnosticBuilder<'_, G>) {
     err.note(
         "this behavior recently changed as a result of a bug fix; \
          see rust-lang/rust#56105 for details",
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 1edf6b11fc3..189e1ba54bc 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -62,14 +62,11 @@ pub fn is_const_evaluatable<'tcx>(
 
         match unexpanded_ct.kind() {
             ty::ConstKind::Expr(_) => {
-                // FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete,
-                // but currently it is not possible to evaluate `ConstKind::Expr` so we are unable
-                // to tell if it is evaluatable or not. For now we just ICE until this is
-                // implemented.
-                Err(NotConstEvaluatable::Error(tcx.dcx().span_delayed_bug(
-                    span,
-                    "evaluating `ConstKind::Expr` is not currently supported",
-                )))
+                // FIXME(generic_const_exprs): we have a fully concrete `ConstKind::Expr`, but
+                // haven't implemented evaluating `ConstKind::Expr` yet, so we are unable to tell
+                // if it is evaluatable or not. As this is unreachable for now, we can simple ICE
+                // here.
+                tcx.dcx().span_bug(span, "evaluating `ConstKind::Expr` is not currently supported");
             }
             ty::ConstKind::Unevaluated(uv) => {
                 let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
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 2d85f84f480..8ae31392b40 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -13,7 +13,7 @@ use hir::def::CtorOf;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, EmissionGuarantee,
     MultiSpan, Style, SuggestionStyle,
 };
 use rustc_hir as hir;
@@ -116,12 +116,12 @@ fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -
 /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but
 /// it can also be an `impl Trait` param that needs to be decomposed to a type
 /// param for cleaner code.
-pub fn suggest_restriction<'tcx>(
+pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(
     tcx: TyCtxt<'tcx>,
     item_id: LocalDefId,
     hir_generics: &hir::Generics<'tcx>,
     msg: &str,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_, G>,
     fn_sig: Option<&hir::FnSig<'_>>,
     projection: Option<&ty::AliasTy<'_>>,
     trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -240,7 +240,7 @@ pub fn suggest_restriction<'tcx>(
 impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_restricting_param_bound(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         associated_ty: Option<(&'static str, Ty<'tcx>)>,
         mut body_id: LocalDefId,
@@ -450,7 +450,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_dereferences(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let mut code = obligation.cause.code();
@@ -743,22 +743,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn get_closure_name(
         &self,
         def_id: DefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         msg: Cow<'static, str>,
     ) -> Option<Symbol> {
-        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<Symbol> {
-            // Get the local name of this closure. This can be inaccurate because
-            // of the possibility of reassignment, but this should be good enough.
-            match &kind {
-                hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
-                    Some(ident.name)
-                }
-                _ => {
-                    err.note(msg);
-                    None
+        let get_name =
+            |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> {
+                // Get the local name of this closure. This can be inaccurate because
+                // of the possibility of reassignment, but this should be good enough.
+                match &kind {
+                    hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, ident, None) => {
+                        Some(ident.name)
+                    }
+                    _ => {
+                        err.note(msg);
+                        None
+                    }
                 }
-            }
-        };
+            };
 
         let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);
         match self.tcx.parent_hir_node(hir_id) {
@@ -778,7 +779,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_fn_call(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         // It doesn't make sense to make this suggestion outside of typeck...
@@ -894,7 +895,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn check_for_binding_assigned_block_without_tail_expression(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let mut span = obligation.cause.span;
@@ -971,7 +972,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_add_clone_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
@@ -1156,7 +1157,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         poly_trait_pred: ty::PolyTraitPredicate<'tcx>,
         has_custom_message: bool,
     ) -> bool {
@@ -1282,6 +1283,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             "the full type name has been written to '{}'",
                             file.display()
                         ));
+                        err.note(format!(
+                            "consider using `--verbose` to print full type name to the console"
+                        ));
                     }
 
                     if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred {
@@ -1374,7 +1378,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     // Suggest borrowing the type
     fn suggest_borrowing_for_object_cast(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         self_ty: Ty<'tcx>,
         target_ty: Ty<'tcx>,
@@ -1410,7 +1414,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_remove_reference(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
         let mut span = obligation.cause.span;
@@ -1529,7 +1533,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         false
     }
 
-    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
+    fn suggest_remove_await(
+        &self,
+        obligation: &PredicateObligation<'tcx>,
+        err: &mut DiagnosticBuilder<'_>,
+    ) {
         let hir = self.tcx.hir();
         if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives()
             && let hir::Node::Expr(expr) = self.tcx.hir_node(*hir_id)
@@ -1599,7 +1607,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_change_mut(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let points_at_arg = matches!(
@@ -1677,7 +1685,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_semicolon_removal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
@@ -1731,7 +1739,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// emitted.
     fn suggest_impl_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) -> bool {
@@ -1913,7 +1921,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn note_conflicting_fn_args(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         cause: &ObligationCauseCode<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -2120,7 +2128,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn suggest_fully_qualified_path(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         item_def_id: DefId,
         span: Span,
         trait_ref: DefId,
@@ -2185,9 +2193,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     ///
     /// Returns `true` if an async-await specific note was added to the diagnostic.
     #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))]
-    fn maybe_note_obligation_cause_for_async_await(
+    fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         obligation: &PredicateObligation<'tcx>,
     ) -> bool {
         let hir = self.tcx.hir();
@@ -2421,9 +2429,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// Unconditionally adds the diagnostic note described in
     /// `maybe_note_obligation_cause_for_async_await`'s documentation comment.
     #[instrument(level = "debug", skip_all)]
-    fn note_obligation_cause_for_async_await(
+    fn note_obligation_cause_for_async_await<G: EmissionGuarantee>(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         interior_or_upvar_span: CoroutineInteriorOrUpvar,
         is_async: bool,
         outer_coroutine: Option<DefId>,
@@ -2656,10 +2664,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         );
     }
 
-    fn note_obligation_cause_code<T>(
+    fn note_obligation_cause_code<G: EmissionGuarantee, T>(
         &self,
         body_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         predicate: T,
         param_env: ty::ParamEnv<'tcx>,
         cause_code: &ObligationCauseCode<'tcx>,
@@ -2861,6 +2869,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         "the full name for the type has been written to '{}'",
                         file.display(),
                     ));
+                    err.note(format!(
+                        "consider using `--verbose` to print the full type name to the console"
+                    ));
                 }
             }
             ObligationCauseCode::RepeatElementCopy {
@@ -3328,6 +3339,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                         "the full type name has been written to '{}'",
                         file.display(),
                     ));
+                    err.note(format!(
+                        "consider using `--verbose` to print the full type name to the console"
+                    ));
                 }
                 let mut parent_predicate = parent_trait_pred;
                 let mut data = &data.derived;
@@ -3381,6 +3395,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                             "the full type name has been written to '{}'",
                             file.display(),
                         ));
+                        err.note(format!(
+                            "consider using `--verbose` to print the full type name to the console"
+                        ));
                     }
                 }
                 // #74711: avoid a stack overflow
@@ -3507,7 +3524,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     )]
     fn suggest_await_before_try(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
@@ -3565,7 +3582,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_floating_point_literal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::PolyTraitRef<'tcx>,
     ) {
         let rhs_span = match obligation.cause.code() {
@@ -3589,7 +3606,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_derive(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
@@ -3655,7 +3672,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn suggest_dereferencing_index(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         if let ObligationCauseCode::ImplDerivedObligation(_) = obligation.cause.code()
@@ -3675,10 +3692,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn note_function_argument_obligation(
+    fn note_function_argument_obligation<G: EmissionGuarantee>(
         &self,
         body_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         arg_hir_id: HirId,
         parent_code: &ObligationCauseCode<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
@@ -3860,11 +3877,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn suggest_option_method_if_applicable(
+    fn suggest_option_method_if_applicable<G: EmissionGuarantee>(
         &self,
         failed_pred: ty::Predicate<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
         expr: &hir::Expr<'_>,
     ) {
         let tcx = self.tcx;
@@ -3934,7 +3951,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn look_for_iterator_item_mistakes(
+    fn look_for_iterator_item_mistakes<G: EmissionGuarantee>(
         &self,
         assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
         typeck_results: &TypeckResults<'tcx>,
@@ -3942,7 +3959,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         param_env: ty::ParamEnv<'tcx>,
         path_segment: &hir::PathSegment<'_>,
         args: &[hir::Expr<'_>],
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         let tcx = self.tcx;
         // Special case for iterator chains, we look at potential failures of `Iterator::Item`
@@ -4037,13 +4054,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    fn point_at_chain(
+    fn point_at_chain<G: EmissionGuarantee>(
         &self,
         expr: &hir::Expr<'_>,
         typeck_results: &TypeckResults<'tcx>,
         type_diffs: Vec<TypeError<'tcx>>,
         param_env: ty::ParamEnv<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         let mut primary_spans = vec![];
         let mut span_labels = vec![];
@@ -4279,7 +4296,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// the array into a slice.
     fn suggest_convert_to_slice(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::PolyTraitRef<'tcx>,
         candidate_impls: &[ImplCandidate<'tcx>],
@@ -4351,7 +4368,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn explain_hrtb_projection(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         pred: ty::PolyTraitPredicate<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
         cause: &ObligationCause<'tcx>,
@@ -4417,7 +4434,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn suggest_desugaring_async_fn_in_trait(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: ty::PolyTraitRef<'tcx>,
     ) {
         // Don't suggest if RTN is active -- we should prefer a where-clause bound instead.
@@ -4508,7 +4525,7 @@ fn hint_missing_borrow<'tcx>(
     found: Ty<'tcx>,
     expected: Ty<'tcx>,
     found_node: Node<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_>,
 ) {
     if matches!(found_node, Node::TraitItem(..)) {
         return;
@@ -4867,9 +4884,9 @@ pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>(
 
 /// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain
 /// they are not allowed and if possible suggest alternatives.
-fn point_at_assoc_type_restriction(
+fn point_at_assoc_type_restriction<G: EmissionGuarantee>(
     tcx: TyCtxt<'_>,
-    err: &mut Diagnostic,
+    err: &mut DiagnosticBuilder<'_, G>,
     self_ty_str: &str,
     trait_name: &str,
     predicate: ty::Predicate<'_>,
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
index 67bd18d7404..7186b96b40d 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs
@@ -21,8 +21,8 @@ use crate::traits::{
 };
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{
-    codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
-    ErrorGuaranteed, MultiSpan, StashKey, StringPart,
+    codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed,
+    FatalError, MultiSpan, StashKey, StringPart,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace, Res};
@@ -185,7 +185,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         predicate: &T,
         span: Span,
         suggest_increasing_limit: bool,
-        mutate: impl FnOnce(&mut Diagnostic),
+        mutate: impl FnOnce(&mut DiagnosticBuilder<'_>),
     ) -> !
     where
         T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
@@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
         mutate(&mut err);
         err.emit();
-
-        self.dcx().abort_if_errors();
-        // FIXME: this should be something like `build_overflow_error_fatal`, which returns
-        // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`.
-        unreachable!(
-            "did not expect compilation to continue after `abort_if_errors`, \
-            since an error was definitely emitted!"
-        );
+        FatalError.raise();
     }
 
     fn build_overflow_error<T>(
@@ -272,7 +265,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         );
     }
 
-    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
+    fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
         let suggested_limit = match self.tcx.recursion_limit() {
             Limit(0) => Limit(2),
             limit => limit * 2,
@@ -1020,7 +1013,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         &self,
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::TraitRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) -> bool {
         let span = obligation.cause.span;
         struct V<'v> {
@@ -1810,7 +1803,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         impl_candidates: &[ImplCandidate<'tcx>],
         trait_ref: ty::PolyTraitRef<'tcx>,
         body_def_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         other: bool,
         param_env: ty::ParamEnv<'tcx>,
     ) -> bool {
@@ -1897,7 +1890,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
 
         let other = if other { "other " } else { "" };
-        let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
+        let report = |candidates: Vec<TraitRef<'tcx>>, err: &mut DiagnosticBuilder<'_>| {
             if candidates.is_empty() {
                 return false;
             }
@@ -2032,7 +2025,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         trait_predicate: ty::Binder<'tcx, ty::TraitPredicate<'tcx>>,
         body_def_id: LocalDefId,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         // This is *almost* equivalent to
         // `obligation.cause.code().peel_derives()`, but it gives us the
@@ -2103,7 +2096,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     /// a probable version mismatch is added to `err`
     fn note_version_mismatch(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         trait_ref: &ty::PolyTraitRef<'tcx>,
     ) -> bool {
         let get_trait_impls = |trait_def_id| {
@@ -2572,7 +2565,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn annotate_source_of_ambiguity(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         ambiguities: &[ambiguity::Ambiguity],
         predicate: ty::Predicate<'tcx>,
     ) {
@@ -2715,7 +2708,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         })
     }
 
-    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) {
+    fn note_obligation_cause(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        obligation: &PredicateObligation<'tcx>,
+    ) {
         // First, attempt to add note to this error with an async-await-specific
         // message, and fall back to regular note otherwise.
         if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
@@ -2744,7 +2741,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip_all)]
     fn suggest_unsized_bound_if_applicable(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         obligation: &PredicateObligation<'tcx>,
     ) {
         let ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) =
@@ -2770,7 +2767,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     }
 
     #[instrument(level = "debug", skip_all)]
-    fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'tcx>) {
+    fn maybe_suggest_unsized_generics(
+        &self,
+        err: &mut DiagnosticBuilder<'_>,
+        span: Span,
+        node: Node<'tcx>,
+    ) {
         let Some(generics) = node.generics() else {
             return;
         };
@@ -2822,7 +2824,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
 
     fn maybe_indirection_for_unsized(
         &self,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         item: &Item<'tcx>,
         param: &GenericParam<'tcx>,
     ) -> bool {
@@ -3016,7 +3018,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn add_tuple_trait_message(
         &self,
         obligation_cause_code: &ObligationCauseCode<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
     ) {
         match obligation_cause_code {
             ObligationCauseCode::RustCall => {
@@ -3041,7 +3043,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         obligation: &PredicateObligation<'tcx>,
         trait_ref: ty::PolyTraitRef<'tcx>,
         trait_predicate: &ty::PolyTraitPredicate<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         span: Span,
         is_fn_trait: bool,
         suggested: bool,
@@ -3122,7 +3124,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn add_help_message_for_fn_trait(
         &self,
         trait_ref: ty::PolyTraitRef<'tcx>,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_>,
         implemented_kind: ty::ClosureKind,
         params: ty::Binder<'tcx, Ty<'tcx>>,
     ) {
@@ -3178,7 +3180,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
     fn maybe_add_note_for_unsatisfied_const(
         &self,
         _trait_predicate: &ty::PolyTraitPredicate<'tcx>,
-        _err: &mut Diagnostic,
+        _err: &mut DiagnosticBuilder<'_>,
         _span: Span,
     ) -> UnsatisfiedConst {
         let unsatisfied_const = UnsatisfiedConst(false);
@@ -3304,7 +3306,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         expected_trait_ref.self_ty().error_reported()?;
 
         let Some(found_trait_ty) = found_trait_ref.self_ty().no_bound_vars() else {
-            return Err(self.dcx().delayed_bug("bound vars outside binder"));
+            self.dcx().bug("bound vars outside binder");
         };
 
         let found_did = match *found_trait_ty.kind() {
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 7ad94274634..fd981130af8 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -1,5 +1,6 @@
 use crate::infer::{InferCtxt, TyOrConstInferVar};
 use crate::traits::error_reporting::TypeErrCtxtExt;
+use crate::traits::normalize::normalize_with_depth_to;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::obligation_forest::ProcessResult;
 use rustc_data_structures::obligation_forest::{Error, ForestObligation, Outcome};
@@ -312,7 +313,7 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
 
         if obligation.predicate.has_projections() {
             let mut obligations = Vec::new();
-            let predicate = crate::traits::project::try_normalize_with_depth_to(
+            let predicate = normalize_with_depth_to(
                 &mut self.selcx,
                 obligation.param_env,
                 obligation.cause.clone(),
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 7caaccab63b..cac53796747 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -9,6 +9,7 @@ mod engine;
 pub mod error_reporting;
 mod fulfill;
 pub mod misc;
+pub mod normalize;
 mod object_safety;
 pub mod outlives_bounds;
 pub mod project;
@@ -40,17 +41,15 @@ use rustc_span::Span;
 use std::fmt::Debug;
 use std::ops::ControlFlow;
 
-pub(crate) use self::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
-
 pub use self::coherence::{add_placeholder_note, orphan_check, overlapping_impls};
 pub use self::coherence::{OrphanCheckErr, OverlapResult};
 pub use self::engine::{ObligationCtxt, TraitEngineExt};
 pub use self::fulfill::{FulfillmentContext, PendingPredicateObligation};
+pub use self::normalize::NormalizeExt;
 pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
 pub use self::object_safety::object_safety_violations_for_assoc_item;
 pub use self::object_safety::ObjectSafetyViolation;
-pub use self::project::NormalizeExt;
 pub use self::project::{normalize_inherent_projection, normalize_projection_type};
 pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
 pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
@@ -68,6 +67,7 @@ pub use self::util::{
 };
 pub use self::util::{expand_trait_aliases, TraitAliasExpander};
 pub use self::util::{get_vtable_index_of_object_method, impl_item_is_final, upcast_choices};
+pub use self::util::{with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
 
 pub use rustc_infer::traits::*;
 
@@ -119,9 +119,7 @@ pub fn predicates_for_generics<'tcx>(
 
 /// Determines whether the type `ty` is known to meet `bound` and
 /// returns true if so. Returns false if `ty` either does not meet
-/// `bound` or is not known to meet bound (note that this is
-/// conservative towards *no impl*, which is the opposite of the
-/// `evaluate` methods).
+/// `bound` or is not known to meet bound.
 pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -129,50 +127,8 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
     def_id: DefId,
 ) -> bool {
     let trait_ref = ty::TraitRef::new(infcx.tcx, def_id, [ty]);
-    pred_known_to_hold_modulo_regions(infcx, param_env, trait_ref)
-}
-
-/// FIXME(@lcnr): this function doesn't seem right and shouldn't exist?
-///
-/// Ping me on zulip if you want to use this method and need help with finding
-/// an appropriate replacement.
-#[instrument(level = "debug", skip(infcx, param_env, pred), ret)]
-fn pred_known_to_hold_modulo_regions<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    pred: impl ToPredicate<'tcx>,
-) -> bool {
-    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, pred);
-
-    let result = infcx.evaluate_obligation_no_overflow(&obligation);
-    debug!(?result);
-
-    if result.must_apply_modulo_regions() {
-        true
-    } else if result.may_apply() {
-        // Sometimes obligations are ambiguous because the recursive evaluator
-        // is not smart enough, so we fall back to fulfillment when we're not certain
-        // that an obligation holds or not. Even still, we must make sure that
-        // the we do no inference in the process of checking this obligation.
-        let goal = infcx.resolve_vars_if_possible((obligation.predicate, obligation.param_env));
-        infcx.probe(|_| {
-            let ocx = ObligationCtxt::new(infcx);
-            ocx.register_obligation(obligation);
-
-            let errors = ocx.select_all_or_error();
-            match errors.as_slice() {
-                // Only known to hold if we did no inference.
-                [] => infcx.shallow_resolve(goal) == goal,
-
-                errors => {
-                    debug!(?errors);
-                    false
-                }
-            }
-        })
-    } else {
-        false
-    }
+    let obligation = Obligation::new(infcx.tcx, ObligationCause::dummy(), param_env, trait_ref);
+    infcx.predicate_must_hold_modulo_regions(&obligation)
 }
 
 #[instrument(level = "debug", skip(tcx, elaborated_env))]
@@ -216,7 +172,9 @@ fn do_normalize_predicates<'tcx>(
     // the normalized predicates.
     let errors = infcx.resolve_regions(&outlives_env);
     if !errors.is_empty() {
-        tcx.dcx().span_delayed_bug(
+        // @lcnr: Let's still ICE here for now. I want a test case
+        // for that.
+        tcx.dcx().span_bug(
             span,
             format!("failed region resolution while normalizing {elaborated_env:?}: {errors:?}"),
         );
diff --git a/compiler/rustc_trait_selection/src/traits/normalize.rs b/compiler/rustc_trait_selection/src/traits/normalize.rs
new file mode 100644
index 00000000000..429e5a5d7a4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/normalize.rs
@@ -0,0 +1,419 @@
+//! Deeply normalize types using the old trait solver.
+use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_infer::infer::at::At;
+use rustc_infer::infer::InferOk;
+use rustc_infer::traits::PredicateObligation;
+use rustc_infer::traits::{FulfillmentError, Normalized, Obligation, TraitEngine};
+use rustc_middle::traits::{ObligationCause, ObligationCauseCode, Reveal};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFolder};
+use rustc_middle::ty::{TypeFoldable, TypeSuperFoldable, TypeVisitable, TypeVisitableExt};
+
+use super::error_reporting::TypeErrCtxtExt;
+use super::SelectionContext;
+use super::{project, with_replaced_escaping_bound_vars, BoundVarReplacer, PlaceholderReplacer};
+
+#[extension(pub trait NormalizeExt<'tcx>)]
+impl<'tcx> At<'_, 'tcx> {
+    /// Normalize a value using the `AssocTypeNormalizer`.
+    ///
+    /// This normalization should be used when the type contains inference variables or the
+    /// projection may be fallible.
+    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
+        if self.infcx.next_trait_solver() {
+            InferOk { value, obligations: Vec::new() }
+        } else {
+            let mut selcx = SelectionContext::new(self.infcx);
+            let Normalized { value, obligations } =
+                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
+            InferOk { value, obligations }
+        }
+    }
+
+    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
+    /// the current environment. In the new solver this errors in case normalization
+    /// fails or is ambiguous.
+    ///
+    /// In the old solver this simply uses `normalizes` and adds the nested obligations
+    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
+    /// same goals in both a temporary and the shared context which negatively impacts
+    /// performance as these don't share caching.
+    ///
+    /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize`
+    /// in the new solver, but because of performance reasons, we currently reuse an
+    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
+    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
+    /// rename this function to `At::fully_normalize`.
+    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
+        self,
+        value: T,
+        fulfill_cx: &mut dyn TraitEngine<'tcx>,
+    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
+        if self.infcx.next_trait_solver() {
+            crate::solve::deeply_normalize(self, value)
+        } else {
+            let value = self
+                .normalize(value)
+                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
+            let errors = fulfill_cx.select_where_possible(self.infcx);
+            let value = self.infcx.resolve_vars_if_possible(value);
+            if errors.is_empty() { Ok(value) } else { Err(errors) }
+        }
+    }
+}
+
+/// As `normalize`, but with a custom depth.
+pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+) -> Normalized<'tcx, T>
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    let mut obligations = Vec::new();
+    let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
+    Normalized { value, obligations }
+}
+
+#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
+pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    depth: usize,
+    value: T,
+    obligations: &mut Vec<PredicateObligation<'tcx>>,
+) -> T
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+{
+    debug!(obligations.len = obligations.len());
+    let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
+    let result = ensure_sufficient_stack(|| normalizer.fold(value));
+    debug!(?result, obligations.len = normalizer.obligations.len());
+    debug!(?normalizer.obligations,);
+    result
+}
+
+pub(super) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
+    value: &T,
+    reveal: Reveal,
+) -> bool {
+    let mut flags = ty::TypeFlags::HAS_TY_PROJECTION
+        | ty::TypeFlags::HAS_TY_WEAK
+        | ty::TypeFlags::HAS_TY_INHERENT
+        | ty::TypeFlags::HAS_CT_PROJECTION;
+
+    match reveal {
+        Reveal::UserFacing => {}
+        Reveal::All => flags |= ty::TypeFlags::HAS_TY_OPAQUE,
+    }
+
+    value.has_type_flags(flags)
+}
+
+struct AssocTypeNormalizer<'a, 'b, 'tcx> {
+    selcx: &'a mut SelectionContext<'b, 'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    cause: ObligationCause<'tcx>,
+    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    depth: usize,
+    universes: Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn new(
+        selcx: &'a mut SelectionContext<'b, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        cause: ObligationCause<'tcx>,
+        depth: usize,
+        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
+    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
+        debug_assert!(!selcx.infcx.next_trait_solver());
+        AssocTypeNormalizer { selcx, param_env, cause, obligations, depth, universes: vec![] }
+    }
+
+    fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
+        let value = self.selcx.infcx.resolve_vars_if_possible(value);
+        debug!(?value);
+
+        assert!(
+            !value.has_escaping_bound_vars(),
+            "Normalizing {value:?} without wrapping in a `Binder`"
+        );
+
+        if !needs_normalization(&value, self.param_env.reveal()) {
+            value
+        } else {
+            value.fold_with(self)
+        }
+    }
+}
+
+impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.selcx.tcx()
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.universes.push(None);
+        let t = t.super_fold_with(self);
+        self.universes.pop();
+        t
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        if !needs_normalization(&ty, self.param_env.reveal()) {
+            return ty;
+        }
+
+        let (kind, data) = match *ty.kind() {
+            ty::Alias(kind, alias_ty) => (kind, alias_ty),
+            _ => return ty.super_fold_with(self),
+        };
+
+        // We try to be a little clever here as a performance optimization in
+        // cases where there are nested projections under binders.
+        // For example:
+        // ```
+        // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
+        // ```
+        // We normalize the args on the projection before the projecting, but
+        // if we're naive, we'll
+        //   replace bound vars on inner, project inner, replace placeholders on inner,
+        //   replace bound vars on outer, project outer, replace placeholders on outer
+        //
+        // However, if we're a bit more clever, we can replace the bound vars
+        // on the entire type before normalizing nested projections, meaning we
+        //   replace bound vars on outer, project inner,
+        //   project outer, replace placeholders on outer
+        //
+        // This is possible because the inner `'a` will already be a placeholder
+        // when we need to normalize the inner projection
+        //
+        // On the other hand, this does add a bit of complexity, since we only
+        // replace bound vars if the current type is a `Projection` and we need
+        // to make sure we don't forget to fold the args regardless.
+
+        match kind {
+            ty::Opaque => {
+                // Only normalize `impl Trait` outside of type inference, usually in codegen.
+                match self.param_env.reveal() {
+                    Reveal::UserFacing => ty.super_fold_with(self),
+
+                    Reveal::All => {
+                        let recursion_limit = self.interner().recursion_limit();
+                        if !recursion_limit.value_within_limit(self.depth) {
+                            self.selcx.infcx.err_ctxt().report_overflow_error(
+                                &ty,
+                                self.cause.span,
+                                true,
+                                |_| {},
+                            );
+                        }
+
+                        let args = data.args.fold_with(self);
+                        let generic_ty = self.interner().type_of(data.def_id);
+                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
+                        self.depth += 1;
+                        let folded_ty = self.fold_ty(concrete_ty);
+                        self.depth -= 1;
+                        folded_ty
+                    }
+                }
+            }
+
+            ty::Projection if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+                let data = data.fold_with(self);
+                let normalized_ty = project::normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                );
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty.ty().unwrap()
+            }
+
+            ty::Projection => {
+                // If there are escaping bound vars, we temporarily replace the
+                // bound vars with placeholders. Note though, that in the case
+                // that we still can't project for whatever reason (e.g. self
+                // type isn't known enough), we *can't* register an obligation
+                // and return an inference variable (since then that obligation
+                // would have bound vars and that's a can of worms). Instead,
+                // we just give up and fall back to pretending like we never tried!
+                //
+                // Note: this isn't necessarily the final approach here; we may
+                // want to figure out how to register obligations with escaping vars
+                // or handle this some other way.
+
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let normalized_ty = project::opt_normalize_projection_type(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+                .ok()
+                .flatten()
+                .map(|term| term.ty().unwrap())
+                .map(|normalized_ty| {
+                    PlaceholderReplacer::replace_placeholders(
+                        infcx,
+                        mapped_regions,
+                        mapped_types,
+                        mapped_consts,
+                        &self.universes,
+                        normalized_ty,
+                    )
+                })
+                .unwrap_or_else(|| ty.super_fold_with(self));
+
+                debug!(
+                    ?self.depth,
+                    ?ty,
+                    ?normalized_ty,
+                    obligations.len = ?self.obligations.len(),
+                    "AssocTypeNormalizer: normalized type"
+                );
+                normalized_ty
+            }
+            ty::Weak => {
+                let recursion_limit = self.interner().recursion_limit();
+                if !recursion_limit.value_within_limit(self.depth) {
+                    self.selcx.infcx.err_ctxt().report_overflow_error(
+                        &ty,
+                        self.cause.span,
+                        false,
+                        |diag| {
+                            diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
+                        },
+                    );
+                }
+
+                let infcx = self.selcx.infcx;
+                self.obligations.extend(
+                    infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
+                        |(mut predicate, span)| {
+                            if data.has_escaping_bound_vars() {
+                                (predicate, ..) = BoundVarReplacer::replace_bound_vars(
+                                    infcx,
+                                    &mut self.universes,
+                                    predicate,
+                                );
+                            }
+                            let mut cause = self.cause.clone();
+                            cause.map_code(|code| {
+                                ObligationCauseCode::TypeAlias(code, span, data.def_id)
+                            });
+                            Obligation::new(infcx.tcx, cause, self.param_env, predicate)
+                        },
+                    ),
+                );
+                self.depth += 1;
+                let res = infcx
+                    .tcx
+                    .type_of(data.def_id)
+                    .instantiate(infcx.tcx, data.args)
+                    .fold_with(self);
+                self.depth -= 1;
+                res
+            }
+
+            ty::Inherent if !data.has_escaping_bound_vars() => {
+                // This branch is *mostly* just an optimization: when we don't
+                // have escaping bound vars, we don't need to replace them with
+                // placeholders (see branch below). *Also*, we know that we can
+                // register an obligation to *later* project, since we know
+                // there won't be bound vars there.
+
+                let data = data.fold_with(self);
+
+                project::normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                )
+            }
+
+            ty::Inherent => {
+                let infcx = self.selcx.infcx;
+                let (data, mapped_regions, mapped_types, mapped_consts) =
+                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
+                let data = data.fold_with(self);
+                let ty = project::normalize_inherent_projection(
+                    self.selcx,
+                    self.param_env,
+                    data,
+                    self.cause.clone(),
+                    self.depth,
+                    self.obligations,
+                );
+
+                PlaceholderReplacer::replace_placeholders(
+                    infcx,
+                    mapped_regions,
+                    mapped_types,
+                    mapped_consts,
+                    &self.universes,
+                    ty,
+                )
+            }
+        }
+    }
+
+    #[instrument(skip(self), level = "debug")]
+    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let tcx = self.selcx.tcx();
+        if tcx.features().generic_const_exprs
+            || !needs_normalization(&constant, self.param_env.reveal())
+        {
+            constant
+        } else {
+            let constant = constant.super_fold_with(self);
+            debug!(?constant, ?self.param_env);
+            with_replaced_escaping_bound_vars(
+                self.selcx.infcx,
+                &mut self.universes,
+                constant,
+                |constant| constant.normalize(tcx, self.param_env),
+            )
+        }
+    }
+
+    #[inline]
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
+            p.super_fold_with(self)
+        } else {
+            p
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
index 6825dd4ac71..c4110df45db 100644
--- a/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
+++ b/compiler/rustc_trait_selection/src/traits/outlives_bounds.rs
@@ -62,9 +62,10 @@ fn implied_outlives_bounds<'a, 'tcx>(
     };
 
     let mut constraints = QueryRegionConstraints::default();
+    let span = infcx.tcx.def_span(body_id);
     let Ok(InferOk { value: mut bounds, obligations }) = infcx
         .instantiate_nll_query_response_and_region_obligations(
-            &ObligationCause::dummy(),
+            &ObligationCause::dummy_with_span(span),
             param_env,
             &canonical_var_values,
             canonical_result,
@@ -80,8 +81,6 @@ fn implied_outlives_bounds<'a, 'tcx>(
     bounds.retain(|bound| !bound.has_placeholders());
 
     if !constraints.is_empty() {
-        let span = infcx.tcx.def_span(body_id);
-
         debug!(?constraints);
         if !constraints.member_constraints.is_empty() {
             span_bug!(span, "{:#?}", constraints.member_constraints);
@@ -101,7 +100,7 @@ fn implied_outlives_bounds<'a, 'tcx>(
 
         let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
-            infcx.dcx().span_delayed_bug(
+            infcx.dcx().span_bug(
                 span,
                 "implied_outlives_bounds failed to solve obligations from instantiation",
             );
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 279c0003187..f8de19043e1 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1,5 +1,7 @@
 //! Code for projecting associated types out of trait references.
 
+use std::ops::ControlFlow;
+
 use super::check_args_compatible;
 use super::specialization_graph;
 use super::translate_args;
@@ -18,8 +20,9 @@ use rustc_middle::traits::ImplSourceUserDefinedData;
 
 use crate::errors::InherentProjectionNormalizationOverflow;
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
-use crate::infer::{BoundRegionConversionTime, InferCtxt, InferOk};
-use crate::traits::error_reporting::TypeErrCtxtExt as _;
+use crate::infer::{BoundRegionConversionTime, InferOk};
+use crate::traits::normalize::normalize_with_depth;
+use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
 use crate::traits::select::ProjectionMatchesProjection;
 use rustc_data_structures::sso::SsoHashSet;
@@ -27,21 +30,14 @@ use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorGuaranteed;
 use rustc_hir::def::DefKind;
 use rustc_hir::lang_items::LangItem;
-use rustc_infer::infer::at::At;
 use rustc_infer::infer::resolve::OpportunisticRegionResolver;
 use rustc_infer::infer::DefineOpaqueTypes;
-use rustc_infer::traits::FulfillmentError;
-use rustc_infer::traits::ObligationCauseCode;
-use rustc_infer::traits::TraitEngine;
 use rustc_middle::traits::select::OverflowError;
-use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_middle::ty::fold::TypeFoldable;
 use rustc_middle::ty::visit::{MaxUniverse, TypeVisitable, TypeVisitableExt};
 use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt};
 use rustc_span::symbol::sym;
 
-use std::collections::BTreeMap;
-use std::ops::ControlFlow;
-
 pub use rustc_middle::traits::Reveal;
 
 pub type PolyProjectionObligation<'tcx> = Obligation<'tcx, ty::PolyProjectionPredicate<'tcx>>;
@@ -52,55 +48,6 @@ pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::AliasTy<'tcx>>;
 
 pub(super) struct InProgress;
 
-#[extension(pub trait NormalizeExt<'tcx>)]
-impl<'tcx> At<'_, 'tcx> {
-    /// Normalize a value using the `AssocTypeNormalizer`.
-    ///
-    /// This normalization should be used when the type contains inference variables or the
-    /// projection may be fallible.
-    fn normalize<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> InferOk<'tcx, T> {
-        if self.infcx.next_trait_solver() {
-            InferOk { value, obligations: Vec::new() }
-        } else {
-            let mut selcx = SelectionContext::new(self.infcx);
-            let Normalized { value, obligations } =
-                normalize_with_depth(&mut selcx, self.param_env, self.cause.clone(), 0, value);
-            InferOk { value, obligations }
-        }
-    }
-
-    /// Deeply normalizes `value`, replacing all aliases which can by normalized in
-    /// the current environment. In the new solver this errors in case normalization
-    /// fails or is ambiguous. This only normalizes opaque types with `Reveal::All`.
-    ///
-    /// In the old solver this simply uses `normalizes` and adds the nested obligations
-    /// to the `fulfill_cx`. This is necessary as we otherwise end up recomputing the
-    /// same goals in both a temporary and the shared context which negatively impacts
-    /// performance as these don't share caching.
-    ///
-    /// FIXME(-Znext-solver): This has the same behavior as `traits::fully_normalize`
-    /// in the new solver, but because of performance reasons, we currently reuse an
-    /// existing fulfillment context in the old solver. Once we also eagerly prove goals with
-    /// the old solver or have removed the old solver, remove `traits::fully_normalize` and
-    /// rename this function to `At::fully_normalize`.
-    fn deeply_normalize<T: TypeFoldable<TyCtxt<'tcx>>>(
-        self,
-        value: T,
-        fulfill_cx: &mut dyn TraitEngine<'tcx>,
-    ) -> Result<T, Vec<FulfillmentError<'tcx>>> {
-        if self.infcx.next_trait_solver() {
-            crate::solve::deeply_normalize(self, value)
-        } else {
-            let value = self
-                .normalize(value)
-                .into_value_registering_obligations(self.infcx, &mut *fulfill_cx);
-            let errors = fulfill_cx.select_where_possible(self.infcx);
-            let value = self.infcx.resolve_vars_if_possible(value);
-            if errors.is_empty() { Ok(value) } else { Err(errors) }
-        }
-    }
-}
-
 /// When attempting to resolve `<T as TraitRef>::Name` ...
 #[derive(Debug)]
 pub enum ProjectionError<'tcx> {
@@ -338,770 +285,6 @@ fn project_and_unify_type<'cx, 'tcx>(
     }
 }
 
-/// As `normalize`, but with a custom depth.
-pub(crate) fn normalize_with_depth<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-) -> Normalized<'tcx, T>
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    let mut obligations = Vec::new();
-    let value = normalize_with_depth_to(selcx, param_env, cause, depth, value, &mut obligations);
-    Normalized { value, obligations }
-}
-
-#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub(crate) fn normalize_with_depth_to<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
-) -> T
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    debug!(obligations.len = obligations.len());
-    let mut normalizer = AssocTypeNormalizer::new(selcx, param_env, cause, depth, obligations);
-    let result = ensure_sufficient_stack(|| normalizer.fold(value));
-    debug!(?result, obligations.len = normalizer.obligations.len());
-    debug!(?normalizer.obligations,);
-    result
-}
-
-#[instrument(level = "info", skip(selcx, param_env, cause, obligations))]
-pub(crate) fn try_normalize_with_depth_to<'a, 'b, 'tcx, T>(
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    depth: usize,
-    value: T,
-    obligations: &mut Vec<PredicateObligation<'tcx>>,
-) -> T
-where
-    T: TypeFoldable<TyCtxt<'tcx>>,
-{
-    debug!(obligations.len = obligations.len());
-    let mut normalizer = AssocTypeNormalizer::new_without_eager_inference_replacement(
-        selcx,
-        param_env,
-        cause,
-        depth,
-        obligations,
-    );
-    let result = ensure_sufficient_stack(|| normalizer.fold(value));
-    debug!(?result, obligations.len = normalizer.obligations.len());
-    debug!(?normalizer.obligations,);
-    result
-}
-
-pub(crate) fn needs_normalization<'tcx, T: TypeVisitable<TyCtxt<'tcx>>>(
-    value: &T,
-    reveal: Reveal,
-) -> bool {
-    match reveal {
-        Reveal::UserFacing => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
-        Reveal::All => value.has_type_flags(
-            ty::TypeFlags::HAS_TY_PROJECTION
-                | ty::TypeFlags::HAS_TY_INHERENT
-                | ty::TypeFlags::HAS_TY_OPAQUE
-                | ty::TypeFlags::HAS_CT_PROJECTION,
-        ),
-    }
-}
-
-struct AssocTypeNormalizer<'a, 'b, 'tcx> {
-    selcx: &'a mut SelectionContext<'b, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    cause: ObligationCause<'tcx>,
-    obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    depth: usize,
-    universes: Vec<Option<ty::UniverseIndex>>,
-    /// If true, when a projection is unable to be completed, an inference
-    /// variable will be created and an obligation registered to project to that
-    /// inference variable. Also, constants will be eagerly evaluated.
-    eager_inference_replacement: bool,
-}
-
-impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
-    fn new(
-        selcx: &'a mut SelectionContext<'b, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        cause: ObligationCause<'tcx>,
-        depth: usize,
-        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
-        debug_assert!(!selcx.infcx.next_trait_solver());
-        AssocTypeNormalizer {
-            selcx,
-            param_env,
-            cause,
-            obligations,
-            depth,
-            universes: vec![],
-            eager_inference_replacement: true,
-        }
-    }
-
-    fn new_without_eager_inference_replacement(
-        selcx: &'a mut SelectionContext<'b, 'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        cause: ObligationCause<'tcx>,
-        depth: usize,
-        obligations: &'a mut Vec<PredicateObligation<'tcx>>,
-    ) -> AssocTypeNormalizer<'a, 'b, 'tcx> {
-        AssocTypeNormalizer {
-            selcx,
-            param_env,
-            cause,
-            obligations,
-            depth,
-            universes: vec![],
-            eager_inference_replacement: false,
-        }
-    }
-
-    fn fold<T: TypeFoldable<TyCtxt<'tcx>>>(&mut self, value: T) -> T {
-        let value = self.selcx.infcx.resolve_vars_if_possible(value);
-        debug!(?value);
-
-        assert!(
-            !value.has_escaping_bound_vars(),
-            "Normalizing {value:?} without wrapping in a `Binder`"
-        );
-
-        if !needs_normalization(&value, self.param_env.reveal()) {
-            value
-        } else {
-            value.fold_with(self)
-        }
-    }
-}
-
-impl<'a, 'b, 'tcx> TypeFolder<TyCtxt<'tcx>> for AssocTypeNormalizer<'a, 'b, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.selcx.tcx()
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.universes.push(None);
-        let t = t.super_fold_with(self);
-        self.universes.pop();
-        t
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        if !needs_normalization(&ty, self.param_env.reveal()) {
-            return ty;
-        }
-
-        let (kind, data) = match *ty.kind() {
-            ty::Alias(kind, alias_ty) => (kind, alias_ty),
-            _ => return ty.super_fold_with(self),
-        };
-
-        // We try to be a little clever here as a performance optimization in
-        // cases where there are nested projections under binders.
-        // For example:
-        // ```
-        // for<'a> fn(<T as Foo>::One<'a, Box<dyn Bar<'a, Item=<T as Foo>::Two<'a>>>>)
-        // ```
-        // We normalize the args on the projection before the projecting, but
-        // if we're naive, we'll
-        //   replace bound vars on inner, project inner, replace placeholders on inner,
-        //   replace bound vars on outer, project outer, replace placeholders on outer
-        //
-        // However, if we're a bit more clever, we can replace the bound vars
-        // on the entire type before normalizing nested projections, meaning we
-        //   replace bound vars on outer, project inner,
-        //   project outer, replace placeholders on outer
-        //
-        // This is possible because the inner `'a` will already be a placeholder
-        // when we need to normalize the inner projection
-        //
-        // On the other hand, this does add a bit of complexity, since we only
-        // replace bound vars if the current type is a `Projection` and we need
-        // to make sure we don't forget to fold the args regardless.
-
-        match kind {
-            ty::Opaque => {
-                // Only normalize `impl Trait` outside of type inference, usually in codegen.
-                match self.param_env.reveal() {
-                    Reveal::UserFacing => ty.super_fold_with(self),
-
-                    Reveal::All => {
-                        let recursion_limit = self.interner().recursion_limit();
-                        if !recursion_limit.value_within_limit(self.depth) {
-                            self.selcx.infcx.err_ctxt().report_overflow_error(
-                                &ty,
-                                self.cause.span,
-                                true,
-                                |_| {},
-                            );
-                        }
-
-                        let args = data.args.fold_with(self);
-                        let generic_ty = self.interner().type_of(data.def_id);
-                        let concrete_ty = generic_ty.instantiate(self.interner(), args);
-                        self.depth += 1;
-                        let folded_ty = self.fold_ty(concrete_ty);
-                        self.depth -= 1;
-                        folded_ty
-                    }
-                }
-            }
-
-            ty::Projection if !data.has_escaping_bound_vars() => {
-                // This branch is *mostly* just an optimization: when we don't
-                // have escaping bound vars, we don't need to replace them with
-                // placeholders (see branch below). *Also*, we know that we can
-                // register an obligation to *later* project, since we know
-                // there won't be bound vars there.
-                let data = data.fold_with(self);
-                let normalized_ty = if self.eager_inference_replacement {
-                    normalize_projection_type(
-                        self.selcx,
-                        self.param_env,
-                        data,
-                        self.cause.clone(),
-                        self.depth,
-                        self.obligations,
-                    )
-                } else {
-                    opt_normalize_projection_type(
-                        self.selcx,
-                        self.param_env,
-                        data,
-                        self.cause.clone(),
-                        self.depth,
-                        self.obligations,
-                    )
-                    .ok()
-                    .flatten()
-                    .unwrap_or_else(|| ty.super_fold_with(self).into())
-                };
-                debug!(
-                    ?self.depth,
-                    ?ty,
-                    ?normalized_ty,
-                    obligations.len = ?self.obligations.len(),
-                    "AssocTypeNormalizer: normalized type"
-                );
-                normalized_ty.ty().unwrap()
-            }
-
-            ty::Projection => {
-                // If there are escaping bound vars, we temporarily replace the
-                // bound vars with placeholders. Note though, that in the case
-                // that we still can't project for whatever reason (e.g. self
-                // type isn't known enough), we *can't* register an obligation
-                // and return an inference variable (since then that obligation
-                // would have bound vars and that's a can of worms). Instead,
-                // we just give up and fall back to pretending like we never tried!
-                //
-                // Note: this isn't necessarily the final approach here; we may
-                // want to figure out how to register obligations with escaping vars
-                // or handle this some other way.
-
-                let infcx = self.selcx.infcx;
-                let (data, mapped_regions, mapped_types, mapped_consts) =
-                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-                let data = data.fold_with(self);
-                let normalized_ty = opt_normalize_projection_type(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                )
-                .ok()
-                .flatten()
-                .map(|term| term.ty().unwrap())
-                .map(|normalized_ty| {
-                    PlaceholderReplacer::replace_placeholders(
-                        infcx,
-                        mapped_regions,
-                        mapped_types,
-                        mapped_consts,
-                        &self.universes,
-                        normalized_ty,
-                    )
-                })
-                .unwrap_or_else(|| ty.super_fold_with(self));
-
-                debug!(
-                    ?self.depth,
-                    ?ty,
-                    ?normalized_ty,
-                    obligations.len = ?self.obligations.len(),
-                    "AssocTypeNormalizer: normalized type"
-                );
-                normalized_ty
-            }
-            ty::Weak => {
-                let recursion_limit = self.interner().recursion_limit();
-                if !recursion_limit.value_within_limit(self.depth) {
-                    self.selcx.infcx.err_ctxt().report_overflow_error(
-                        &ty,
-                        self.cause.span,
-                        false,
-                        |diag| {
-                            diag.note(crate::fluent_generated::trait_selection_ty_alias_overflow);
-                        },
-                    );
-                }
-
-                let infcx = self.selcx.infcx;
-                self.obligations.extend(
-                    infcx.tcx.predicates_of(data.def_id).instantiate_own(infcx.tcx, data.args).map(
-                        |(mut predicate, span)| {
-                            if data.has_escaping_bound_vars() {
-                                (predicate, ..) = BoundVarReplacer::replace_bound_vars(
-                                    infcx,
-                                    &mut self.universes,
-                                    predicate,
-                                );
-                            }
-                            let mut cause = self.cause.clone();
-                            cause.map_code(|code| {
-                                ObligationCauseCode::TypeAlias(code, span, data.def_id)
-                            });
-                            Obligation::new(infcx.tcx, cause, self.param_env, predicate)
-                        },
-                    ),
-                );
-                self.depth += 1;
-                let res = infcx
-                    .tcx
-                    .type_of(data.def_id)
-                    .instantiate(infcx.tcx, data.args)
-                    .fold_with(self);
-                self.depth -= 1;
-                res
-            }
-
-            ty::Inherent if !data.has_escaping_bound_vars() => {
-                // This branch is *mostly* just an optimization: when we don't
-                // have escaping bound vars, we don't need to replace them with
-                // placeholders (see branch below). *Also*, we know that we can
-                // register an obligation to *later* project, since we know
-                // there won't be bound vars there.
-
-                let data = data.fold_with(self);
-
-                // FIXME(inherent_associated_types): Do we need to honor `self.eager_inference_replacement`
-                // here like `ty::Projection`?
-                normalize_inherent_projection(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                )
-            }
-
-            ty::Inherent => {
-                let infcx = self.selcx.infcx;
-                let (data, mapped_regions, mapped_types, mapped_consts) =
-                    BoundVarReplacer::replace_bound_vars(infcx, &mut self.universes, data);
-                let data = data.fold_with(self);
-                let ty = normalize_inherent_projection(
-                    self.selcx,
-                    self.param_env,
-                    data,
-                    self.cause.clone(),
-                    self.depth,
-                    self.obligations,
-                );
-
-                PlaceholderReplacer::replace_placeholders(
-                    infcx,
-                    mapped_regions,
-                    mapped_types,
-                    mapped_consts,
-                    &self.universes,
-                    ty,
-                )
-            }
-        }
-    }
-
-    #[instrument(skip(self), level = "debug")]
-    fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        let tcx = self.selcx.tcx();
-        if tcx.features().generic_const_exprs
-            || !needs_normalization(&constant, self.param_env.reveal())
-        {
-            constant
-        } else {
-            let constant = constant.super_fold_with(self);
-            debug!(?constant, ?self.param_env);
-            with_replaced_escaping_bound_vars(
-                self.selcx.infcx,
-                &mut self.universes,
-                constant,
-                |constant| constant.normalize(tcx, self.param_env),
-            )
-        }
-    }
-
-    #[inline]
-    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.allow_normalization() && needs_normalization(&p, self.param_env.reveal()) {
-            p.super_fold_with(self)
-        } else {
-            p
-        }
-    }
-}
-
-pub struct BoundVarReplacer<'me, 'tcx> {
-    infcx: &'me InferCtxt<'tcx>,
-    // These three maps track the bound variable that were replaced by placeholders. It might be
-    // nice to remove these since we already have the `kind` in the placeholder; we really just need
-    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
-    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
-    // the depth of binders we've passed here.
-    current_index: ty::DebruijnIndex,
-    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
-    // we don't actually create a universe until we see a bound var we have to replace.
-    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
-}
-
-/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
-/// and then replaces these placeholders with the original bound variables in the result.
-///
-/// In most places, bound variables should be replaced right when entering a binder, making
-/// this function unnecessary. However, normalization currently does not do that, so we have
-/// to do this lazily.
-///
-/// You should not add any additional uses of this function, at least not without first
-/// discussing it with t-types.
-///
-/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
-/// normalization as well, at which point this function will be unnecessary and can be removed.
-pub fn with_replaced_escaping_bound_vars<
-    'a,
-    'tcx,
-    T: TypeFoldable<TyCtxt<'tcx>>,
-    R: TypeFoldable<TyCtxt<'tcx>>,
->(
-    infcx: &'a InferCtxt<'tcx>,
-    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
-    value: T,
-    f: impl FnOnce(T) -> R,
-) -> R {
-    if value.has_escaping_bound_vars() {
-        let (value, mapped_regions, mapped_types, mapped_consts) =
-            BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
-        let result = f(value);
-        PlaceholderReplacer::replace_placeholders(
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            universe_indices,
-            result,
-        )
-    } else {
-        f(value)
-    }
-}
-
-impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
-    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
-    /// use a binding level above `universe_indices.len()`, we fail.
-    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
-        infcx: &'me InferCtxt<'tcx>,
-        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
-        value: T,
-    ) -> (
-        T,
-        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    ) {
-        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
-        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
-        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
-
-        let mut replacer = BoundVarReplacer {
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            current_index: ty::INNERMOST,
-            universe_indices,
-        };
-
-        let value = value.fold_with(&mut replacer);
-
-        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
-    }
-
-    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
-        let infcx = self.infcx;
-        let index =
-            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
-        let universe = self.universe_indices[index].unwrap_or_else(|| {
-            for i in self.universe_indices.iter_mut().take(index + 1) {
-                *i = i.or_else(|| Some(infcx.create_next_universe()))
-            }
-            self.universe_indices[index].unwrap()
-        });
-        universe
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
-    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        match *r {
-            ty::ReBound(debruijn, _)
-                if debruijn.as_usize()
-                    >= self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderRegion { universe, bound: br };
-                self.mapped_regions.insert(p, br);
-                ty::Region::new_placeholder(self.infcx.tcx, p)
-            }
-            _ => r,
-        }
-    }
-
-    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
-        match *t.kind() {
-            ty::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderType { universe, bound: bound_ty };
-                self.mapped_types.insert(p, bound_ty);
-                Ty::new_placeholder(self.infcx.tcx, p)
-            }
-            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
-            _ => t,
-        }
-    }
-
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        match ct.kind() {
-            ty::ConstKind::Bound(debruijn, _)
-                if debruijn.as_usize() + 1
-                    > self.current_index.as_usize() + self.universe_indices.len() =>
-            {
-                bug!(
-                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
-                    self.universe_indices
-                );
-            }
-            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
-                let universe = self.universe_for(debruijn);
-                let p = ty::PlaceholderConst { universe, bound: bound_const };
-                self.mapped_consts.insert(p, bound_const);
-                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
-            }
-            _ => ct.super_fold_with(self),
-        }
-    }
-
-    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
-        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
-    }
-}
-
-/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
-pub struct PlaceholderReplacer<'me, 'tcx> {
-    infcx: &'me InferCtxt<'tcx>,
-    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-    universe_indices: &'me [Option<ty::UniverseIndex>],
-    current_index: ty::DebruijnIndex,
-}
-
-impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
-    pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
-        infcx: &'me InferCtxt<'tcx>,
-        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
-        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
-        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
-        universe_indices: &'me [Option<ty::UniverseIndex>],
-        value: T,
-    ) -> T {
-        let mut replacer = PlaceholderReplacer {
-            infcx,
-            mapped_regions,
-            mapped_types,
-            mapped_consts,
-            universe_indices,
-            current_index: ty::INNERMOST,
-        };
-        value.fold_with(&mut replacer)
-    }
-}
-
-impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
-    fn interner(&self) -> TyCtxt<'tcx> {
-        self.infcx.tcx
-    }
-
-    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: ty::Binder<'tcx, T>,
-    ) -> ty::Binder<'tcx, T> {
-        if !t.has_placeholders() && !t.has_infer() {
-            return t;
-        }
-        self.current_index.shift_in(1);
-        let t = t.super_fold_with(self);
-        self.current_index.shift_out(1);
-        t
-    }
-
-    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
-        let r1 = match *r0 {
-            ty::ReVar(vid) => self
-                .infcx
-                .inner
-                .borrow_mut()
-                .unwrap_region_constraints()
-                .opportunistic_resolve_var(self.infcx.tcx, vid),
-            _ => r0,
-        };
-
-        let r2 = match *r1 {
-            ty::RePlaceholder(p) => {
-                let replace_var = self.mapped_regions.get(&p);
-                match replace_var {
-                    Some(replace_var) => {
-                        let index = self
-                            .universe_indices
-                            .iter()
-                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                        let db = ty::DebruijnIndex::from_usize(
-                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                        );
-                        ty::Region::new_bound(self.interner(), db, *replace_var)
-                    }
-                    None => r1,
-                }
-            }
-            _ => r1,
-        };
-
-        debug!(?r0, ?r1, ?r2, "fold_region");
-
-        r2
-    }
-
-    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
-        let ty = self.infcx.shallow_resolve(ty);
-        match *ty.kind() {
-            ty::Placeholder(p) => {
-                let replace_var = self.mapped_types.get(&p);
-                match replace_var {
-                    Some(replace_var) => {
-                        let index = self
-                            .universe_indices
-                            .iter()
-                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                        let db = ty::DebruijnIndex::from_usize(
-                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                        );
-                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
-                    }
-                    None => {
-                        if ty.has_infer() {
-                            ty.super_fold_with(self)
-                        } else {
-                            ty
-                        }
-                    }
-                }
-            }
-
-            _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
-            _ => ty,
-        }
-    }
-
-    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
-        let ct = self.infcx.shallow_resolve(ct);
-        if let ty::ConstKind::Placeholder(p) = ct.kind() {
-            let replace_var = self.mapped_consts.get(&p);
-            match replace_var {
-                Some(replace_var) => {
-                    let index = self
-                        .universe_indices
-                        .iter()
-                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
-                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
-                    let db = ty::DebruijnIndex::from_usize(
-                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
-                    );
-                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
-                }
-                None => {
-                    if ct.has_infer() {
-                        ct.super_fold_with(self)
-                    } else {
-                        ct
-                    }
-                }
-            }
-        } else {
-            ct.super_fold_with(self)
-        }
-    }
-}
-
 /// The guts of `normalize`: normalize a specific projection like `<T
 /// as Trait>::Item`. The result is always a type (and possibly
 /// additional obligations). If ambiguity arises, which implies that
@@ -1146,7 +329,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
 /// function takes an obligations vector and appends to it directly, which is
 /// slightly uglier but avoids the need for an extra short-lived allocation.
 #[instrument(level = "debug", skip(selcx, param_env, cause, obligations))]
-fn opt_normalize_projection_type<'a, 'b, 'tcx>(
+pub(super) fn opt_normalize_projection_type<'a, 'b, 'tcx>(
     selcx: &'a mut SelectionContext<'b, 'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     projection_ty: ty::AliasTy<'tcx>,
@@ -1250,14 +433,14 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>(
             let projected_term = selcx.infcx.resolve_vars_if_possible(projected_term);
 
             let mut result = if projected_term.has_projections() {
-                let mut normalizer = AssocTypeNormalizer::new(
+                let normalized_ty = normalize_with_depth_to(
                     selcx,
                     param_env,
                     cause,
                     depth + 1,
+                    projected_term,
                     &mut projected_obligations,
                 );
-                let normalized_ty = normalizer.fold(projected_term);
 
                 Normalized { value: normalized_ty, obligations: projected_obligations }
             } else {
@@ -1462,11 +645,9 @@ pub fn compute_inherent_assoc_ty_args<'a, 'b, 'tcx>(
     match selcx.infcx.at(&cause, param_env).eq(DefineOpaqueTypes::No, impl_ty, self_ty) {
         Ok(mut ok) => obligations.append(&mut ok.obligations),
         Err(_) => {
-            tcx.dcx().span_delayed_bug(
+            tcx.dcx().span_bug(
                 cause.span,
-                format!(
-                    "{self_ty:?} was a subtype of {impl_ty:?} during selection but now it is not"
-                ),
+                format!("{self_ty:?} was equal to {impl_ty:?} during selection but now it is not"),
             );
         }
     }
@@ -2011,7 +1192,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                     obligation.cause.span,
                     format!("Cannot project an associated type from `{impl_source:?}`"),
                 );
-                return Err(());
+                return Err(())
             }
         };
 
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 0f6c0abd280..70fd0b7e50b 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -1,12 +1,13 @@
 //! Code for the 'normalization' query. This consists of a wrapper
 //! which folds deeply, invoking the underlying
-//! `normalize_projection_ty` query when it encounters projections.
+//! `normalize_canonicalized_projection_ty` query when it encounters projections.
 
 use crate::infer::at::At;
 use crate::infer::canonical::OriginalQueryValues;
 use crate::infer::{InferCtxt, InferOk};
 use crate::traits::error_reporting::TypeErrCtxtExt;
-use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
+use crate::traits::normalize::needs_normalization;
+use crate::traits::{BoundVarReplacer, PlaceholderReplacer};
 use crate::traits::{ObligationCause, PredicateObligation, Reveal};
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -270,9 +271,9 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 debug!("QueryNormalizer: c_data = {:#?}", c_data);
                 debug!("QueryNormalizer: orig_values = {:#?}", orig_values);
                 let result = match kind {
-                    ty::Projection => tcx.normalize_projection_ty(c_data),
-                    ty::Weak => tcx.normalize_weak_ty(c_data),
-                    ty::Inherent => tcx.normalize_inherent_projection_ty(c_data),
+                    ty::Projection => tcx.normalize_canonicalized_projection_ty(c_data),
+                    ty::Weak => tcx.normalize_canonicalized_weak_ty(c_data),
+                    ty::Inherent => tcx.normalize_canonicalized_inherent_projection_ty(c_data),
                     kind => unreachable!("did not expect {kind:?} due to match arm above"),
                 }?;
                 // We don't expect ambiguity.
@@ -307,10 +308,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
                 } else {
                     result.normalized_ty
                 };
-                // `tcx.normalize_projection_ty` may normalize to a type that still has
-                // unevaluated consts, so keep normalizing here if that's the case.
-                // Similarly, `tcx.normalize_weak_ty` will only unwrap one layer of type
-                // and we need to continue folding it to reveal the TAIT behind it.
+                // `tcx.normalize_canonicalized_projection_ty` may normalize to a type that
+                // still has unevaluated consts, so keep normalizing here if that's the case.
+                // Similarly, `tcx.normalize_canonicalized_weak_ty` will only unwrap one layer
+                // of type and we need to continue folding it to reveal the TAIT behind it.
                 if res != ty
                     && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) || kind == ty::Weak)
                 {
@@ -335,7 +336,7 @@ impl<'cx, 'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for QueryNormalizer<'cx, 'tcx>
 
         let constant = constant.try_super_fold_with(self)?;
         debug!(?constant, ?self.param_env);
-        Ok(crate::traits::project::with_replaced_escaping_bound_vars(
+        Ok(crate::traits::with_replaced_escaping_bound_vars(
             self.infcx,
             &mut self.universes,
             constant,
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
index fb09f094d37..12ee778ee05 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs
@@ -190,10 +190,9 @@ where
                 }
             }
             if !progress {
-                return Err(infcx.dcx().span_delayed_bug(
-                    span,
-                    format!("ambiguity processing {obligations:?} from {self:?}"),
-                ));
+                infcx
+                    .dcx()
+                    .span_bug(span, format!("ambiguity processing {obligations:?} from {self:?}"));
             }
         }
 
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index e4a70c537d2..f76be876948 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -18,7 +18,7 @@ use rustc_middle::ty::{
 };
 use rustc_span::def_id::DefId;
 
-use crate::traits::project::{normalize_with_depth, normalize_with_depth_to};
+use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
 use crate::traits::util::{self, closure_trait_ref_and_return_type};
 use crate::traits::vtable::{
     count_own_vtable_entries, prepare_vtable_segments, vtable_trait_first_method_offset,
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5bcf46a96ed..1146f869fc1 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -8,7 +8,6 @@ use self::SelectionCandidate::*;
 use super::coherence::{self, Conflict};
 use super::const_evaluatable;
 use super::project;
-use super::project::normalize_with_depth_to;
 use super::project::ProjectionTyObligation;
 use super::util;
 use super::util::closure_trait_ref_and_return_type;
@@ -22,14 +21,15 @@ use super::{
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
 use crate::solve::InferCtxtSelectExt;
 use crate::traits::error_reporting::TypeErrCtxtExt;
-use crate::traits::project::try_normalize_with_depth_to;
+use crate::traits::normalize::normalize_with_depth;
+use crate::traits::normalize::normalize_with_depth_to;
 use crate::traits::project::ProjectAndUnifyResult;
 use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
 use crate::traits::Unimplemented;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Diagnostic;
+use rustc_errors::{DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::BoundRegionConversionTime;
@@ -70,7 +70,10 @@ pub enum IntercrateAmbiguityCause<'tcx> {
 impl<'tcx> IntercrateAmbiguityCause<'tcx> {
     /// Emits notes when the overlap is caused by complex intercrate ambiguities.
     /// See #23980 for details.
-    pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) {
+    pub fn add_intercrate_ambiguity_hint<G: EmissionGuarantee>(
+        &self,
+        err: &mut DiagnosticBuilder<'_, G>,
+    ) {
         err.note(self.intercrate_ambiguity_hint());
     }
 
@@ -1070,7 +1073,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 && fresh_trait_pred.is_global()
             {
                 let mut nested_obligations = Vec::new();
-                let predicate = try_normalize_with_depth_to(
+                let predicate = normalize_with_depth_to(
                     this,
                     param_env,
                     obligation.cause.clone(),
@@ -1662,7 +1665,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
 
         let Normalized { value: trait_bound, obligations: _ } = ensure_sufficient_stack(|| {
-            project::normalize_with_depth(
+            normalize_with_depth(
                 self,
                 obligation.param_env,
                 obligation.cause.clone(),
@@ -1718,7 +1721,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         );
         let infer_projection = if potentially_unnormalized_candidates {
             ensure_sufficient_stack(|| {
-                project::normalize_with_depth_to(
+                normalize_with_depth_to(
                     self,
                     obligation.param_env,
                     obligation.cause.clone(),
@@ -2383,7 +2386,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
                 let placeholder_ty = self.infcx.enter_forall_and_leak_universe(ty);
                 let Normalized { value: normalized_ty, mut obligations } =
                     ensure_sufficient_stack(|| {
-                        project::normalize_with_depth(
+                        normalize_with_depth(
                             self,
                             param_env,
                             cause.clone(),
@@ -2480,7 +2483,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 
         let Normalized { value: impl_trait_ref, obligations: mut nested_obligations } =
             ensure_sufficient_stack(|| {
-                project::normalize_with_depth(
+                normalize_with_depth(
                     self,
                     obligation.param_env,
                     obligation.cause.clone(),
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index e1a49ecf1b6..56bc2f2cf25 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -20,7 +20,7 @@ use crate::traits::{
     self, coherence, FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt,
 };
 use rustc_data_structures::fx::FxIndexSet;
-use rustc_errors::{codes::*, DelayDm, Diagnostic};
+use rustc_errors::{codes::*, DelayDm, DiagnosticBuilder, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::ty::{self, ImplSubject, Ty, TyCtxt, TypeVisitableExt};
 use rustc_middle::ty::{GenericArgs, GenericArgsRef};
@@ -395,11 +395,11 @@ fn report_conflicting_impls<'tcx>(
     // Work to be done after we've built the DiagnosticBuilder. We have to define it
     // now because the lint emit methods don't return back the DiagnosticBuilder
     // that's passed in.
-    fn decorate<'tcx>(
+    fn decorate<'tcx, G: EmissionGuarantee>(
         tcx: TyCtxt<'tcx>,
         overlap: &OverlapError<'tcx>,
         impl_span: Span,
-        err: &mut Diagnostic,
+        err: &mut DiagnosticBuilder<'_, G>,
     ) {
         if (overlap.trait_ref, overlap.self_ty).references_error() {
             err.downgrade_to_delayed_bug();
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index af172eb0713..d66c4004ef5 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -1,11 +1,14 @@
+use std::collections::BTreeMap;
+
 use super::NormalizeExt;
 use super::{ObligationCause, PredicateObligation, SelectionContext};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::Diagnostic;
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::InferOk;
+use rustc_infer::infer::{InferCtxt, InferOk};
 use rustc_middle::ty::GenericArgsRef;
 use rustc_middle::ty::{self, ImplSubject, ToPredicate, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::{TypeFoldable, TypeFolder, TypeSuperFoldable};
 use rustc_span::Span;
 use smallvec::SmallVec;
 
@@ -43,7 +46,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
     /// trait aliases.
     pub fn label_with_exp_info(
         &self,
-        diag: &mut Diagnostic,
+        diag: &mut DiagnosticBuilder<'_>,
         top_label: &'static str,
         use_desc: &str,
     ) {
@@ -382,3 +385,336 @@ pub fn check_args_compatible<'tcx>(
     let args = &args[0..generics.count().min(args.len())];
     check_args_compatible_inner(tcx, generics, args)
 }
+
+/// Executes `f` on `value` after replacing all escaping bound variables with placeholders
+/// and then replaces these placeholders with the original bound variables in the result.
+///
+/// In most places, bound variables should be replaced right when entering a binder, making
+/// this function unnecessary. However, normalization currently does not do that, so we have
+/// to do this lazily.
+///
+/// You should not add any additional uses of this function, at least not without first
+/// discussing it with t-types.
+///
+/// FIXME(@lcnr): We may even consider experimenting with eagerly replacing bound vars during
+/// normalization as well, at which point this function will be unnecessary and can be removed.
+pub fn with_replaced_escaping_bound_vars<
+    'a,
+    'tcx,
+    T: TypeFoldable<TyCtxt<'tcx>>,
+    R: TypeFoldable<TyCtxt<'tcx>>,
+>(
+    infcx: &'a InferCtxt<'tcx>,
+    universe_indices: &'a mut Vec<Option<ty::UniverseIndex>>,
+    value: T,
+    f: impl FnOnce(T) -> R,
+) -> R {
+    if value.has_escaping_bound_vars() {
+        let (value, mapped_regions, mapped_types, mapped_consts) =
+            BoundVarReplacer::replace_bound_vars(infcx, universe_indices, value);
+        let result = f(value);
+        PlaceholderReplacer::replace_placeholders(
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            result,
+        )
+    } else {
+        f(value)
+    }
+}
+
+pub struct BoundVarReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    // These three maps track the bound variable that were replaced by placeholders. It might be
+    // nice to remove these since we already have the `kind` in the placeholder; we really just need
+    // the `var` (but we *could* bring that into scope if we were to track them as we pass them).
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    // The current depth relative to *this* folding, *not* the entire normalization. In other words,
+    // the depth of binders we've passed here.
+    current_index: ty::DebruijnIndex,
+    // The `UniverseIndex` of the binding levels above us. These are optional, since we are lazy:
+    // we don't actually create a universe until we see a bound var we have to replace.
+    universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+}
+
+impl<'me, 'tcx> BoundVarReplacer<'me, 'tcx> {
+    /// Returns `Some` if we *were* able to replace bound vars. If there are any bound vars that
+    /// use a binding level above `universe_indices.len()`, we fail.
+    pub fn replace_bound_vars<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        universe_indices: &'me mut Vec<Option<ty::UniverseIndex>>,
+        value: T,
+    ) -> (
+        T,
+        BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    ) {
+        let mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion> = BTreeMap::new();
+        let mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy> = BTreeMap::new();
+        let mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar> = BTreeMap::new();
+
+        let mut replacer = BoundVarReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            current_index: ty::INNERMOST,
+            universe_indices,
+        };
+
+        let value = value.fold_with(&mut replacer);
+
+        (value, replacer.mapped_regions, replacer.mapped_types, replacer.mapped_consts)
+    }
+
+    fn universe_for(&mut self, debruijn: ty::DebruijnIndex) -> ty::UniverseIndex {
+        let infcx = self.infcx;
+        let index =
+            self.universe_indices.len() + self.current_index.as_usize() - debruijn.as_usize() - 1;
+        let universe = self.universe_indices[index].unwrap_or_else(|| {
+            for i in self.universe_indices.iter_mut().take(index + 1) {
+                *i = i.or_else(|| Some(infcx.create_next_universe()))
+            }
+            self.universe_indices[index].unwrap()
+        });
+        universe
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for BoundVarReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        match *r {
+            ty::ReBound(debruijn, _)
+                if debruijn.as_usize()
+                    >= self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {r:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ReBound(debruijn, br) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderRegion { universe, bound: br };
+                self.mapped_regions.insert(p, br);
+                ty::Region::new_placeholder(self.infcx.tcx, p)
+            }
+            _ => r,
+        }
+    }
+
+    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+        match *t.kind() {
+            ty::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {t:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::Bound(debruijn, bound_ty) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderType { universe, bound: bound_ty };
+                self.mapped_types.insert(p, bound_ty);
+                Ty::new_placeholder(self.infcx.tcx, p)
+            }
+            _ if t.has_vars_bound_at_or_above(self.current_index) => t.super_fold_with(self),
+            _ => t,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        match ct.kind() {
+            ty::ConstKind::Bound(debruijn, _)
+                if debruijn.as_usize() + 1
+                    > self.current_index.as_usize() + self.universe_indices.len() =>
+            {
+                bug!(
+                    "Bound vars {ct:#?} outside of `self.universe_indices`: {:#?}",
+                    self.universe_indices
+                );
+            }
+            ty::ConstKind::Bound(debruijn, bound_const) if debruijn >= self.current_index => {
+                let universe = self.universe_for(debruijn);
+                let p = ty::PlaceholderConst { universe, bound: bound_const };
+                self.mapped_consts.insert(p, bound_const);
+                ty::Const::new_placeholder(self.infcx.tcx, p, ct.ty())
+            }
+            _ => ct.super_fold_with(self),
+        }
+    }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
+}
+
+/// The inverse of [`BoundVarReplacer`]: replaces placeholders with the bound vars from which they came.
+pub struct PlaceholderReplacer<'me, 'tcx> {
+    infcx: &'me InferCtxt<'tcx>,
+    mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+    mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+    mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+    universe_indices: &'me [Option<ty::UniverseIndex>],
+    current_index: ty::DebruijnIndex,
+}
+
+impl<'me, 'tcx> PlaceholderReplacer<'me, 'tcx> {
+    pub fn replace_placeholders<T: TypeFoldable<TyCtxt<'tcx>>>(
+        infcx: &'me InferCtxt<'tcx>,
+        mapped_regions: BTreeMap<ty::PlaceholderRegion, ty::BoundRegion>,
+        mapped_types: BTreeMap<ty::PlaceholderType, ty::BoundTy>,
+        mapped_consts: BTreeMap<ty::PlaceholderConst, ty::BoundVar>,
+        universe_indices: &'me [Option<ty::UniverseIndex>],
+        value: T,
+    ) -> T {
+        let mut replacer = PlaceholderReplacer {
+            infcx,
+            mapped_regions,
+            mapped_types,
+            mapped_consts,
+            universe_indices,
+            current_index: ty::INNERMOST,
+        };
+        value.fold_with(&mut replacer)
+    }
+}
+
+impl<'tcx> TypeFolder<TyCtxt<'tcx>> for PlaceholderReplacer<'_, 'tcx> {
+    fn interner(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn fold_binder<T: TypeFoldable<TyCtxt<'tcx>>>(
+        &mut self,
+        t: ty::Binder<'tcx, T>,
+    ) -> ty::Binder<'tcx, T> {
+        if !t.has_placeholders() && !t.has_infer() {
+            return t;
+        }
+        self.current_index.shift_in(1);
+        let t = t.super_fold_with(self);
+        self.current_index.shift_out(1);
+        t
+    }
+
+    fn fold_region(&mut self, r0: ty::Region<'tcx>) -> ty::Region<'tcx> {
+        let r1 = match *r0 {
+            ty::ReVar(vid) => self
+                .infcx
+                .inner
+                .borrow_mut()
+                .unwrap_region_constraints()
+                .opportunistic_resolve_var(self.infcx.tcx, vid),
+            _ => r0,
+        };
+
+        let r2 = match *r1 {
+            ty::RePlaceholder(p) => {
+                let replace_var = self.mapped_regions.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        ty::Region::new_bound(self.interner(), db, *replace_var)
+                    }
+                    None => r1,
+                }
+            }
+            _ => r1,
+        };
+
+        debug!(?r0, ?r1, ?r2, "fold_region");
+
+        r2
+    }
+
+    fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+        let ty = self.infcx.shallow_resolve(ty);
+        match *ty.kind() {
+            ty::Placeholder(p) => {
+                let replace_var = self.mapped_types.get(&p);
+                match replace_var {
+                    Some(replace_var) => {
+                        let index = self
+                            .universe_indices
+                            .iter()
+                            .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                            .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                        let db = ty::DebruijnIndex::from_usize(
+                            self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                        );
+                        Ty::new_bound(self.infcx.tcx, db, *replace_var)
+                    }
+                    None => {
+                        if ty.has_infer() {
+                            ty.super_fold_with(self)
+                        } else {
+                            ty
+                        }
+                    }
+                }
+            }
+
+            _ if ty.has_placeholders() || ty.has_infer() => ty.super_fold_with(self),
+            _ => ty,
+        }
+    }
+
+    fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+        let ct = self.infcx.shallow_resolve(ct);
+        if let ty::ConstKind::Placeholder(p) = ct.kind() {
+            let replace_var = self.mapped_consts.get(&p);
+            match replace_var {
+                Some(replace_var) => {
+                    let index = self
+                        .universe_indices
+                        .iter()
+                        .position(|u| matches!(u, Some(pu) if *pu == p.universe))
+                        .unwrap_or_else(|| bug!("Unexpected placeholder universe."));
+                    let db = ty::DebruijnIndex::from_usize(
+                        self.universe_indices.len() - index + self.current_index.as_usize() - 1,
+                    );
+                    ty::Const::new_bound(self.infcx.tcx, db, *replace_var, ct.ty())
+                }
+                None => {
+                    if ct.has_infer() {
+                        ct.super_fold_with(self)
+                    } else {
+                        ct
+                    }
+                }
+            }
+        } else {
+            ct.super_fold_with(self)
+        }
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index c727ef53d55..15059bc6613 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -313,7 +313,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
             // Don't normalize the whole obligation, the param env is either
             // already normalized, or we're currently normalizing the
             // param_env. Either way we should only normalize the predicate.
-            let normalized_predicate = traits::project::normalize_with_depth_to(
+            let normalized_predicate = traits::normalize::normalize_with_depth_to(
                 &mut selcx,
                 param_env,
                 cause.clone(),
diff --git a/compiler/rustc_traits/src/normalize_projection_ty.rs b/compiler/rustc_traits/src/normalize_projection_ty.rs
index 07089d5f19e..92a19fb9119 100644
--- a/compiler/rustc_traits/src/normalize_projection_ty.rs
+++ b/compiler/rustc_traits/src/normalize_projection_ty.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty::{ParamEnvAnd, TyCtxt};
 use rustc_trait_selection::infer::InferCtxtBuilderExt;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::query::{
-    normalize::NormalizationResult, CanonicalProjectionGoal, NoSolution,
+    normalize::NormalizationResult, CanonicalAliasGoal, NoSolution,
 };
 use rustc_trait_selection::traits::{
     self, FulfillmentErrorCode, ObligationCause, SelectionContext,
@@ -13,22 +13,23 @@ use rustc_trait_selection::traits::{
 
 pub(crate) fn provide(p: &mut Providers) {
     *p = Providers {
-        normalize_projection_ty,
-        normalize_weak_ty,
-        normalize_inherent_projection_ty,
+        normalize_canonicalized_projection_ty,
+        normalize_canonicalized_weak_ty,
+        normalize_canonicalized_inherent_projection_ty,
         ..*p
     };
 }
 
-fn normalize_projection_ty<'tcx>(
+fn normalize_canonicalized_projection_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_projection_ty(goal={:#?})", goal);
 
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,
         |ocx, ParamEnvAnd { param_env, value: goal }| {
+            debug_assert!(!ocx.infcx.next_trait_solver());
             let selcx = &mut SelectionContext::new(ocx.infcx);
             let cause = ObligationCause::dummy();
             let mut obligations = vec![];
@@ -45,36 +46,35 @@ fn normalize_projection_ty<'tcx>(
             // are recursive (given some generic parameters of the opaque's type variables).
             // In that case, we may only realize a cycle error when calling
             // `normalize_erasing_regions` in mono.
-            if !ocx.infcx.next_trait_solver() {
-                let errors = ocx.select_where_possible();
-                if !errors.is_empty() {
-                    // Rustdoc may attempt to normalize type alias types which are not
-                    // well-formed. Rustdoc also normalizes types that are just not
-                    // well-formed, since we don't do as much HIR analysis (checking
-                    // that impl vars are constrained by the signature, for example).
-                    if !tcx.sess.opts.actually_rustdoc {
-                        for error in &errors {
-                            if let FulfillmentErrorCode::Cycle(cycle) = &error.code {
-                                ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
-                            }
+            let errors = ocx.select_where_possible();
+            if !errors.is_empty() {
+                // Rustdoc may attempt to normalize type alias types which are not
+                // well-formed. Rustdoc also normalizes types that are just not
+                // well-formed, since we don't do as much HIR analysis (checking
+                // that impl vars are constrained by the signature, for example).
+                if !tcx.sess.opts.actually_rustdoc {
+                    for error in &errors {
+                        if let FulfillmentErrorCode::Cycle(cycle) = &error.code {
+                            ocx.infcx.err_ctxt().report_overflow_obligation_cycle(cycle);
                         }
                     }
-                    return Err(NoSolution);
                 }
+                return Err(NoSolution);
             }
-            // FIXME(associated_const_equality): All users of normalize_projection_ty expected
-            // a type, but there is the possibility it could've been a const now. Maybe change
-            // it to a Term later?
+
+            // FIXME(associated_const_equality): All users of normalize_canonicalized_projection_ty
+            // expected a type, but there is the possibility it could've been a const now.
+            // Maybe change it to a Term later?
             Ok(NormalizationResult { normalized_ty: answer.ty().unwrap() })
         },
     )
 }
 
-fn normalize_weak_ty<'tcx>(
+fn normalize_canonicalized_weak_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_weak_ty(goal={:#?})", goal);
 
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,
@@ -96,11 +96,11 @@ fn normalize_weak_ty<'tcx>(
     )
 }
 
-fn normalize_inherent_projection_ty<'tcx>(
+fn normalize_canonicalized_inherent_projection_ty<'tcx>(
     tcx: TyCtxt<'tcx>,
-    goal: CanonicalProjectionGoal<'tcx>,
+    goal: CanonicalAliasGoal<'tcx>,
 ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, NormalizationResult<'tcx>>>, NoSolution> {
-    debug!("normalize_provider(goal={:#?})", goal);
+    debug!("normalize_canonicalized_inherent_projection_ty(goal={:#?})", goal);
 
     tcx.infer_ctxt().enter_canonical_trait_query(
         &goal,
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
index 5fc93d666ab..3e3bccce47f 100644
--- a/compiler/rustc_ty_utils/src/instance.rs
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -246,7 +246,7 @@ fn resolve_associated_item<'tcx>(
                         span: tcx.def_span(trait_item_id),
                     })
                 }
-            } else if tcx.fn_trait_kind_from_def_id(trait_ref.def_id).is_some() {
+            } else if let Some(target_kind) = tcx.fn_trait_kind_from_def_id(trait_ref.def_id) {
                 // FIXME: This doesn't check for malformed libcore that defines, e.g.,
                 // `trait Fn { fn call_once(&self) { .. } }`. This is mostly for extension
                 // methods.
@@ -265,13 +265,7 @@ fn resolve_associated_item<'tcx>(
                 }
                 match *rcvr_args.type_at(0).kind() {
                     ty::Closure(closure_def_id, args) => {
-                        let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
-                        Some(Instance::resolve_closure(
-                            tcx,
-                            closure_def_id,
-                            args,
-                            trait_closure_kind,
-                        ))
+                        Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind))
                     }
                     ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                         def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
@@ -324,13 +318,7 @@ fn resolve_associated_item<'tcx>(
                         }
                     }
                     ty::Closure(closure_def_id, args) => {
-                        let trait_closure_kind = tcx.fn_trait_kind_from_def_id(trait_id).unwrap();
-                        Some(Instance::resolve_closure(
-                            tcx,
-                            closure_def_id,
-                            args,
-                            trait_closure_kind,
-                        ))
+                        Some(Instance::resolve_closure(tcx, closure_def_id, args, target_kind))
                     }
                     ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
                         def: ty::InstanceDef::FnPtrShim(trait_item_id, rcvr_args.type_at(0)),
diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs
index 96f8148bf72..b00330d335e 100644
--- a/compiler/rustc_ty_utils/src/layout.rs
+++ b/compiler/rustc_ty_utils/src/layout.rs
@@ -90,8 +90,7 @@ fn univariant_uninterned<'tcx>(
     let dl = cx.data_layout();
     let pack = repr.pack;
     if pack.is_some() && repr.align.is_some() {
-        cx.tcx.dcx().delayed_bug("struct cannot be packed and aligned");
-        return Err(cx.tcx.arena.alloc(LayoutError::Unknown(ty)));
+        cx.tcx.dcx().bug("struct cannot be packed and aligned");
     }
 
     cx.univariant(dl, fields, repr, kind).ok_or_else(|| error(cx, LayoutError::SizeOverflow(ty)))
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index 329cf32cad5..86c7551882a 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -337,7 +337,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ImplTraitInAssocTypeCollector<'tcx> {
                                 .instantiate(self.0.tcx, impl_args)
                                 .visit_with(self);
                         } else {
-                            self.0.tcx.dcx().span_delayed_bug(
+                            self.0.tcx.dcx().span_bug(
                                 self.0.tcx.def_span(assoc.def_id),
                                 "item had incorrect args",
                             );
diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs
index 1da26cfc242..b38ef2ad84d 100644
--- a/compiler/rustc_type_ir/src/flags.rs
+++ b/compiler/rustc_type_ir/src/flags.rs
@@ -69,32 +69,35 @@ bitflags! {
 
         /// Does this have `Projection`?
         const HAS_TY_PROJECTION           = 1 << 10;
-        /// Does this have `Inherent`?
-        const HAS_TY_INHERENT             = 1 << 11;
+        /// Does this have `Weak`?
+        const HAS_TY_WEAK                 = 1 << 11;
         /// Does this have `Opaque`?
         const HAS_TY_OPAQUE               = 1 << 12;
+        /// Does this have `Inherent`?
+        const HAS_TY_INHERENT             = 1 << 13;
         /// Does this have `ConstKind::Unevaluated`?
-        const HAS_CT_PROJECTION           = 1 << 13;
+        const HAS_CT_PROJECTION           = 1 << 14;
 
         /// Could this type be normalized further?
         const HAS_PROJECTION              = TypeFlags::HAS_TY_PROJECTION.bits()
+                                          | TypeFlags::HAS_TY_WEAK.bits()
                                           | TypeFlags::HAS_TY_OPAQUE.bits()
                                           | TypeFlags::HAS_TY_INHERENT.bits()
                                           | TypeFlags::HAS_CT_PROJECTION.bits();
 
         /// Is an error type/const reachable?
-        const HAS_ERROR                   = 1 << 14;
+        const HAS_ERROR                   = 1 << 15;
 
         /// Does this have any region that "appears free" in the type?
         /// Basically anything but `ReBound` and `ReErased`.
-        const HAS_FREE_REGIONS            = 1 << 15;
+        const HAS_FREE_REGIONS            = 1 << 16;
 
         /// Does this have any `ReBound` regions?
-        const HAS_RE_BOUND                = 1 << 16;
+        const HAS_RE_BOUND                = 1 << 17;
         /// Does this have any `Bound` types?
-        const HAS_TY_BOUND                = 1 << 17;
+        const HAS_TY_BOUND                = 1 << 18;
         /// Does this have any `ConstKind::Bound` consts?
-        const HAS_CT_BOUND                = 1 << 18;
+        const HAS_CT_BOUND                = 1 << 19;
         /// Does this have any bound variables?
         /// Used to check if a global bound is safe to evaluate.
         const HAS_BOUND_VARS              = TypeFlags::HAS_RE_BOUND.bits()
@@ -102,22 +105,22 @@ bitflags! {
                                           | TypeFlags::HAS_CT_BOUND.bits();
 
         /// Does this have any `ReErased` regions?
-        const HAS_RE_ERASED               = 1 << 19;
+        const HAS_RE_ERASED               = 1 << 20;
 
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
-        const STILL_FURTHER_SPECIALIZABLE = 1 << 20;
+        const STILL_FURTHER_SPECIALIZABLE = 1 << 21;
 
         /// Does this value have `InferTy::FreshTy/FreshIntTy/FreshFloatTy`?
-        const HAS_TY_FRESH                = 1 << 21;
+        const HAS_TY_FRESH                = 1 << 22;
 
         /// Does this value have `InferConst::Fresh`?
-        const HAS_CT_FRESH                = 1 << 22;
+        const HAS_CT_FRESH                = 1 << 23;
 
         /// Does this have `Coroutine` or `CoroutineWitness`?
-        const HAS_TY_COROUTINE            = 1 << 23;
+        const HAS_TY_COROUTINE            = 1 << 24;
 
         /// Does this have any binders with bound vars (e.g. that need to be anonymized)?
-        const HAS_BINDER_VARS             = 1 << 24;
+        const HAS_BINDER_VARS             = 1 << 25;
     }
 }
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 7728ee0e842..7d2c42a6dbe 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -95,9 +95,6 @@ pub trait Interner: Sized {
     fn mk_bound_ty(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Ty;
     fn mk_bound_region(self, debruijn: DebruijnIndex, var: BoundVar) -> Self::Region;
     fn mk_bound_const(self, debruijn: DebruijnIndex, var: BoundVar, ty: Self::Ty) -> Self::Const;
-
-    /// Assert that an error has been delayed or emitted.
-    fn expect_error_or_delayed_bug();
 }
 
 /// Common capabilities of placeholder kinds
diff --git a/compiler/stable_mir/src/error.rs b/compiler/stable_mir/src/error.rs
index 7085fa937c9..9e3f4936944 100644
--- a/compiler/stable_mir/src/error.rs
+++ b/compiler/stable_mir/src/error.rs
@@ -15,10 +15,8 @@ macro_rules! error {
 /// An error type used to represent an error that has already been reported by the compiler.
 #[derive(Clone, Copy, PartialEq, Eq)]
 pub enum CompilerError<T> {
-    /// Internal compiler error (I.e.: Compiler crashed).
-    ICE,
-    /// Compilation failed.
-    CompilationFailed,
+    /// Compilation failed, either due to normal errors or ICE.
+    Failed,
     /// Compilation was interrupted.
     Interrupted(T),
     /// Compilation skipped. This happens when users invoke rustc to retrieve information such as
@@ -54,8 +52,7 @@ where
 {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         match self {
-            CompilerError::ICE => write!(f, "Internal Compiler Error"),
-            CompilerError::CompilationFailed => write!(f, "Compilation Failed"),
+            CompilerError::Failed => write!(f, "Compilation Failed"),
             CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason}"),
             CompilerError::Skipped => write!(f, "Compilation Skipped"),
         }
@@ -68,8 +65,7 @@ where
 {
     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
         match self {
-            CompilerError::ICE => write!(f, "Internal Compiler Error"),
-            CompilerError::CompilationFailed => write!(f, "Compilation Failed"),
+            CompilerError::Failed => write!(f, "Compilation Failed"),
             CompilerError::Interrupted(reason) => write!(f, "Compilation Interrupted: {reason:?}"),
             CompilerError::Skipped => write!(f, "Compilation Skipped"),
         }