about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-02-14 19:08:48 +0100
committerRalf Jung <post@ralfj.de>2024-02-14 19:08:48 +0100
commit7a086ac8875b45adee3d2cff0f024205eb568d8d (patch)
tree138995ac33229aa6af4c1e84d891314800d71afa /compiler
parent6539a64d73849176ff327b28a55d7d6787f293ef (diff)
parent533e3f0a840cc7d803c508a9732f411ef8682188 (diff)
downloadrust-7a086ac8875b45adee3d2cff0f024205eb568d8d.tar.gz
rust-7a086ac8875b45adee3d2cff0f024205eb568d8d.zip
Merge from rustc
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/Cargo.toml3
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs4
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs124
-rw-r--r--compiler/rustc_borrowck/src/lib.rs3
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs24
-rw-r--r--compiler/rustc_borrowck/src/used_muts.rs3
-rw-r--r--compiler/rustc_codegen_ssa/src/target_features.rs3
-rw-r--r--compiler/rustc_const_eval/src/const_eval/machine.rs3
-rw-r--r--compiler/rustc_const_eval/src/interpret/eval_context.rs4
-rw-r--r--compiler/rustc_const_eval/src/interpret/intern.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs20
-rw-r--r--compiler/rustc_data_structures/src/small_c_str.rs2
-rw-r--r--compiler/rustc_data_structures/src/small_c_str/tests.rs8
-rw-r--r--compiler/rustc_error_messages/src/lib.rs2
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs6
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs3
-rw-r--r--compiler/rustc_errors/src/emitter.rs3
-rw-r--r--compiler/rustc_errors/src/lib.rs249
-rw-r--r--compiler/rustc_expand/src/mbe/diagnostics.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/object_safety.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs8
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs78
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs25
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/unsafety.rs44
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs1
-rw-r--r--compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs40
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs5
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs82
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/confirm.rs9
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/writeback.rs14
-rw-r--r--compiler/rustc_infer/src/infer/at.rs48
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs66
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs14
-rw-r--r--compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs14
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs6
-rw-r--r--compiler/rustc_infer/src/infer/opaque_types/table.rs3
-rw-r--r--compiler/rustc_infer/src/infer/outlives/obligations.rs6
-rw-r--r--compiler/rustc_infer/src/infer/outlives/verify.rs8
-rw-r--r--compiler/rustc_lint/src/early.rs13
-rw-r--r--compiler/rustc_lint/src/unused.rs16
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs3
-rw-r--r--compiler/rustc_llvm/Cargo.toml7
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp2
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp23
-rw-r--r--compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp3
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs2
-rw-r--r--compiler/rustc_middle/src/lint.rs5
-rw-r--r--compiler/rustc_middle/src/ty/consts.rs12
-rw-r--r--compiler/rustc_middle/src/ty/context.rs23
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs11
-rw-r--r--compiler/rustc_middle/src/ty/predicate.rs12
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs11
-rw-r--r--compiler/rustc_middle/src/ty/region.rs13
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs25
-rw-r--r--compiler/rustc_middle/src/ty/util.rs1
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs311
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs132
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs22
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs13
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/move_paths/builder.rs5
-rw-r--r--compiler/rustc_mir_transform/src/check_unsafety.rs9
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans.rs281
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs24
-rw-r--r--compiler/rustc_mir_transform/src/dest_prop.rs3
-rw-r--r--compiler/rustc_mir_transform/src/gvn.rs14
-rw-r--r--compiler/rustc_mir_transform/src/lib.rs2
-rw-r--r--compiler/rustc_next_trait_solver/src/canonicalizer.rs5
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs2
-rw-r--r--compiler/rustc_passes/src/stability.rs8
-rw-r--r--compiler/rustc_query_system/src/query/job.rs20
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs8
-rw-r--r--compiler/rustc_resolve/src/check_unused.rs3
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs62
-rw-r--r--compiler/rustc_session/src/config.rs10
-rw-r--r--compiler/rustc_session/src/session.rs13
-rw-r--r--compiler/rustc_trait_selection/src/solve/alias_relate.rs111
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs54
-rw-r--r--compiler/rustc_trait_selection/src/solve/mod.rs79
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs20
-rw-r--r--compiler/rustc_trait_selection/src/solve/trait_goals.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/auto_trait.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/const_evaluatable.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs17
-rw-r--r--compiler/rustc_trait_selection/src/traits/object_safety.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs12
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs16
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/structural_match.rs7
-rw-r--r--compiler/rustc_type_ir/src/binder.rs7
-rw-r--r--compiler/rustc_type_ir/src/interner.rs26
-rw-r--r--compiler/rustc_type_ir/src/lib.rs2
-rw-r--r--compiler/rustc_type_ir/src/visit.rs389
103 files changed, 1638 insertions, 1300 deletions
diff --git a/compiler/rustc_ast/Cargo.toml b/compiler/rustc_ast/Cargo.toml
index 937ee4bcd42..087f6a192b5 100644
--- a/compiler/rustc_ast/Cargo.toml
+++ b/compiler/rustc_ast/Cargo.toml
@@ -4,9 +4,10 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
+# FIXME: bumping memchr to 2.7.1 causes linker errors in MSVC thin-lto
 # tidy-alphabetical-start
 bitflags = "2.4.1"
-memchr = "2.5.0"
+memchr = "=2.5.0"
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
 rustc_lexer = { path = "../rustc_lexer" }
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index 942aae3d536..73d1d891bb9 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -323,9 +323,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     )
                 }
                 ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
-                ExprKind::Err => {
-                    hir::ExprKind::Err(self.dcx().span_delayed_bug(e.span, "lowered ExprKind::Err"))
-                }
+                ExprKind::Err => hir::ExprKind::Err(self.dcx().has_errors().unwrap()),
                 ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
 
                 ExprKind::Paren(_) | ExprKind::ForLoop { .. } => {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 933372fae4e..87ed47648c8 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1068,7 +1068,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
     fn lower_block_expr_opt(&mut self, span: Span, block: Option<&Block>) -> hir::Expr<'hir> {
         match block {
             Some(block) => self.lower_block_expr(block),
-            None => self.expr_err(span, self.dcx().span_delayed_bug(span, "no block")),
+            None => self.expr_err(span, self.dcx().has_errors().unwrap()),
         }
     }
 
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 480072ce705..6b5fc014240 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1285,9 +1285,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     fn lower_ty_direct(&mut self, t: &Ty, itctx: ImplTraitContext) -> hir::Ty<'hir> {
         let kind = match &t.kind {
             TyKind::Infer => hir::TyKind::Infer,
-            TyKind::Err => {
-                hir::TyKind::Err(self.dcx().span_delayed_bug(t.span, "TyKind::Err lowered"))
-            }
+            TyKind::Err => hir::TyKind::Err(self.dcx().has_errors().unwrap()),
             // Lower the anonymous structs or unions in a nested lowering context.
             //
             // ```
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index d24dc44d2c5..ebc5c17a4c4 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -1593,44 +1593,98 @@ fn deny_equality_constraints(
             }
         }
     }
-    // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
-    if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
-        if let [potential_param, potential_assoc] = &full_path.segments[..] {
-            for param in &generics.params {
-                if param.ident == potential_param.ident {
-                    for bound in &param.bounds {
-                        if let ast::GenericBound::Trait(trait_ref, TraitBoundModifiers::NONE) =
-                            bound
+
+    let mut suggest =
+        |poly: &PolyTraitRef, potential_assoc: &PathSegment, predicate: &WhereEqPredicate| {
+            if let [trait_segment] = &poly.trait_ref.path.segments[..] {
+                let assoc = pprust::path_to_string(&ast::Path::from_ident(potential_assoc.ident));
+                let ty = pprust::ty_to_string(&predicate.rhs_ty);
+                let (args, span) = match &trait_segment.args {
+                    Some(args) => match args.deref() {
+                        ast::GenericArgs::AngleBracketed(args) => {
+                            let Some(arg) = args.args.last() else {
+                                return;
+                            };
+                            (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
+                        }
+                        _ => return,
+                    },
+                    None => (format!("<{assoc} = {ty}>"), trait_segment.span().shrink_to_hi()),
+                };
+                let removal_span = if generics.where_clause.predicates.len() == 1 {
+                    // We're removing th eonly where bound left, remove the whole thing.
+                    generics.where_clause.span
+                } else {
+                    let mut span = predicate.span;
+                    let mut prev: Option<Span> = None;
+                    let mut preds = generics.where_clause.predicates.iter().peekable();
+                    // Find the predicate that shouldn't have been in the where bound list.
+                    while let Some(pred) = preds.next() {
+                        if let WherePredicate::EqPredicate(pred) = pred
+                            && pred.span == predicate.span
                         {
-                            if let [trait_segment] = &trait_ref.trait_ref.path.segments[..] {
-                                let assoc = pprust::path_to_string(&ast::Path::from_ident(
-                                    potential_assoc.ident,
-                                ));
-                                let ty = pprust::ty_to_string(&predicate.rhs_ty);
-                                let (args, span) = match &trait_segment.args {
-                                    Some(args) => match args.deref() {
-                                        ast::GenericArgs::AngleBracketed(args) => {
-                                            let Some(arg) = args.args.last() else {
-                                                continue;
-                                            };
-                                            (format!(", {assoc} = {ty}"), arg.span().shrink_to_hi())
-                                        }
-                                        _ => continue,
-                                    },
-                                    None => (
-                                        format!("<{assoc} = {ty}>"),
-                                        trait_segment.span().shrink_to_hi(),
-                                    ),
-                                };
-                                err.assoc2 = Some(errors::AssociatedSuggestion2 {
-                                    span,
-                                    args,
-                                    predicate: predicate.span,
-                                    trait_segment: trait_segment.ident,
-                                    potential_assoc: potential_assoc.ident,
-                                });
+                            if let Some(next) = preds.peek() {
+                                // This is the first predicate, remove the trailing comma as well.
+                                span = span.with_hi(next.span().lo());
+                            } else if let Some(prev) = prev {
+                                // Remove the previous comma as well.
+                                span = span.with_lo(prev.hi());
                             }
                         }
+                        prev = Some(pred.span());
+                    }
+                    span
+                };
+                err.assoc2 = Some(errors::AssociatedSuggestion2 {
+                    span,
+                    args,
+                    predicate: removal_span,
+                    trait_segment: trait_segment.ident,
+                    potential_assoc: potential_assoc.ident,
+                });
+            }
+        };
+
+    if let TyKind::Path(None, full_path) = &predicate.lhs_ty.kind {
+        // Given `A: Foo, Foo::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+        for bounds in generics.params.iter().map(|p| &p.bounds).chain(
+            generics.where_clause.predicates.iter().filter_map(|pred| match pred {
+                WherePredicate::BoundPredicate(p) => Some(&p.bounds),
+                _ => None,
+            }),
+        ) {
+            for bound in bounds {
+                if let GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
+                    if full_path.segments[..full_path.segments.len() - 1]
+                        .iter()
+                        .map(|segment| segment.ident.name)
+                        .zip(poly.trait_ref.path.segments.iter().map(|segment| segment.ident.name))
+                        .all(|(a, b)| a == b)
+                        && let Some(potential_assoc) = full_path.segments.iter().last()
+                    {
+                        suggest(poly, potential_assoc, predicate);
+                    }
+                }
+            }
+        }
+        // Given `A: Foo, A::Bar = RhsTy`, suggest `A: Foo<Bar = RhsTy>`.
+        if let [potential_param, potential_assoc] = &full_path.segments[..] {
+            for (ident, bounds) in generics.params.iter().map(|p| (p.ident, &p.bounds)).chain(
+                generics.where_clause.predicates.iter().filter_map(|pred| match pred {
+                    WherePredicate::BoundPredicate(p)
+                        if let ast::TyKind::Path(None, path) = &p.bounded_ty.kind
+                            && let [segment] = &path.segments[..] =>
+                    {
+                        Some((segment.ident, &p.bounds))
+                    }
+                    _ => None,
+                }),
+            ) {
+                if ident == potential_param.ident {
+                    for bound in bounds {
+                        if let ast::GenericBound::Trait(poly, TraitBoundModifiers::NONE) = bound {
+                            suggest(poly, potential_assoc, predicate);
+                        }
                     }
                 }
             }
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index e8ffab9307e..6c2a511538d 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -2479,7 +2479,8 @@ mod diags {
             &mut self,
             span: Span,
         ) -> Option<(DiagnosticBuilder<'tcx>, usize)> {
-            self.diags.buffered_mut_errors.remove(&span)
+            // FIXME(#120456) - is `swap_remove` correct?
+            self.diags.buffered_mut_errors.swap_remove(&span)
         }
 
         pub fn buffer_mut_error(&mut self, span: Span, t: DiagnosticBuilder<'tcx>, count: usize) {
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index cfb46f3ac8a..ae4000f02a7 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -1432,7 +1432,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         return;
                     }
                 };
-                let (sig, map) = tcx.instantiate_bound_regions(sig, |br| {
+                let (unnormalized_sig, map) = tcx.instantiate_bound_regions(sig, |br| {
                     use crate::renumber::RegionCtxt;
 
                     let region_ctxt_fn = || {
@@ -1454,7 +1454,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                         region_ctxt_fn,
                     )
                 });
-                debug!(?sig);
+                debug!(?unnormalized_sig);
                 // IMPORTANT: We have to prove well formed for the function signature before
                 // we normalize it, as otherwise types like `<&'a &'b () as Trait>::Assoc`
                 // get normalized away, causing us to ignore the `'b: 'a` bound used by the function.
@@ -1464,7 +1464,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                 //
                 // See #91068 for an example.
                 self.prove_predicates(
-                    sig.inputs_and_output.iter().map(|ty| {
+                    unnormalized_sig.inputs_and_output.iter().map(|ty| {
                         ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(
                             ty.into(),
                         )))
@@ -1472,7 +1472,23 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
                     term_location.to_locations(),
                     ConstraintCategory::Boring,
                 );
-                let sig = self.normalize(sig, term_location);
+
+                let sig = self.normalize(unnormalized_sig, term_location);
+                // HACK(#114936): `WF(sig)` does not imply `WF(normalized(sig))`
+                // with built-in `Fn` implementations, since the impl may not be
+                // well-formed itself.
+                if sig != unnormalized_sig {
+                    self.prove_predicates(
+                        sig.inputs_and_output.iter().map(|ty| {
+                            ty::Binder::dummy(ty::PredicateKind::Clause(
+                                ty::ClauseKind::WellFormed(ty.into()),
+                            ))
+                        }),
+                        term_location.to_locations(),
+                        ConstraintCategory::Boring,
+                    );
+                }
+
                 self.check_call_dest(body, term, &sig, *destination, *target, term_location);
 
                 // The ordinary liveness rules will ensure that all
diff --git a/compiler/rustc_borrowck/src/used_muts.rs b/compiler/rustc_borrowck/src/used_muts.rs
index 81757a62e5b..dea1c7823a5 100644
--- a/compiler/rustc_borrowck/src/used_muts.rs
+++ b/compiler/rustc_borrowck/src/used_muts.rs
@@ -58,7 +58,8 @@ impl GatherUsedMutsVisitor<'_, '_, '_> {
         // be those that were never initialized - we will consider those as being used as
         // they will either have been removed by unreachable code optimizations; or linted
         // as unused variables.
-        self.never_initialized_mut_locals.remove(&into.local);
+        // FIXME(#120456) - is `swap_remove` correct?
+        self.never_initialized_mut_locals.swap_remove(&into.local);
     }
 }
 
diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs
index ee1d548b231..241b0a15f78 100644
--- a/compiler/rustc_codegen_ssa/src/target_features.rs
+++ b/compiler/rustc_codegen_ssa/src/target_features.rs
@@ -106,7 +106,8 @@ fn asm_target_features(tcx: TyCtxt<'_>, did: DefId) -> &FxIndexSet<Symbol> {
         match attrs.instruction_set {
             None => {}
             Some(InstructionSetAttr::ArmA32) => {
-                target_features.remove(&sym::thumb_mode);
+                // FIXME(#120456) - is `swap_remove` correct?
+                target_features.swap_remove(&sym::thumb_mode);
             }
             Some(InstructionSetAttr::ArmT32) => {
                 target_features.insert(sym::thumb_mode);
diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs
index d08985edb76..5019bec388c 100644
--- a/compiler/rustc_const_eval/src/const_eval/machine.rs
+++ b/compiler/rustc_const_eval/src/const_eval/machine.rs
@@ -122,7 +122,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxIndexMap<K, V> {
     where
         K: Borrow<Q>,
     {
-        FxIndexMap::remove(self, k)
+        // FIXME(#120456) - is `swap_remove` correct?
+        FxIndexMap::swap_remove(self, k)
     }
 
     #[inline(always)]
diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 33e96e7faa8..8af69220e03 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -288,7 +288,7 @@ impl<'tcx> fmt::Display for FrameInfo<'tcx> {
             if tcx.def_key(self.instance.def_id()).disambiguated_data.data == DefPathData::Closure {
                 write!(f, "inside closure")
             } else {
-                // Note: this triggers a `good_path_delayed_bug` state, which means that if we ever
+                // Note: this triggers a `must_produce_diag` state, which means that if we ever
                 // get here we must emit a diagnostic. We should never display a `FrameInfo` unless
                 // we actually want to emit a warning or error to the user.
                 write!(f, "inside `{}`", self.instance)
@@ -304,7 +304,7 @@ impl<'tcx> FrameInfo<'tcx> {
             errors::FrameNote { where_: "closure", span, instance: String::new(), times: 0 }
         } else {
             let instance = format!("{}", self.instance);
-            // Note: this triggers a `good_path_delayed_bug` state, which means that if we ever get
+            // Note: this triggers a `must_produce_diag` state, which means that if we ever get
             // here we must emit a diagnostic. We should never display a `FrameInfo` unless we
             // actually want to emit a warning or error to the user.
             errors::FrameNote { where_: "instance", span, instance, times: 0 }
diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs
index 38e7843761b..7feac6156bc 100644
--- a/compiler/rustc_const_eval/src/interpret/intern.rs
+++ b/compiler/rustc_const_eval/src/interpret/intern.rs
@@ -49,7 +49,8 @@ fn intern_shallow<'rt, 'mir, 'tcx, T, M: CompileTimeMachine<'mir, 'tcx, T>>(
 ) -> Result<impl Iterator<Item = CtfeProvenance> + 'tcx, ()> {
     trace!("intern_shallow {:?}", alloc_id);
     // remove allocation
-    let Some((_kind, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) else {
+    // FIXME(#120456) - is `swap_remove` correct?
+    let Some((_kind, mut alloc)) = ecx.memory.alloc_map.swap_remove(&alloc_id) else {
         return Err(());
     };
     // Set allocation mutability as appropriate. This is used by LLVM to put things into
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 5ce6a71c4bd..b5c70538c52 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -117,18 +117,14 @@ struct CfgChecker<'a, 'tcx> {
 impl<'a, 'tcx> CfgChecker<'a, 'tcx> {
     #[track_caller]
     fn fail(&self, location: Location, msg: impl AsRef<str>) {
-        let span = self.body.source_info(location).span;
-        // We use `span_delayed_bug` as we might see broken MIR when other errors have already
-        // occurred.
-        self.tcx.dcx().span_delayed_bug(
-            span,
-            format!(
-                "broken MIR in {:?} ({}) at {:?}:\n{}",
-                self.body.source.instance,
-                self.when,
-                location,
-                msg.as_ref()
-            ),
+        // We might see broken MIR when other errors have already occurred.
+        assert!(
+            self.tcx.dcx().has_errors().is_some(),
+            "broken MIR in {:?} ({}) at {:?}:\n{}",
+            self.body.source.instance,
+            self.when,
+            location,
+            msg.as_ref(),
         );
     }
 
diff --git a/compiler/rustc_data_structures/src/small_c_str.rs b/compiler/rustc_data_structures/src/small_c_str.rs
index 349fd7f9769..809ce3d4483 100644
--- a/compiler/rustc_data_structures/src/small_c_str.rs
+++ b/compiler/rustc_data_structures/src/small_c_str.rs
@@ -82,6 +82,6 @@ impl<'a> FromIterator<&'a str> for SmallCStr {
 
 impl From<&ffi::CStr> for SmallCStr {
     fn from(s: &ffi::CStr) -> Self {
-        Self { data: SmallVec::from_slice(s.to_bytes()) }
+        Self { data: SmallVec::from_slice(s.to_bytes_with_nul()) }
     }
 }
diff --git a/compiler/rustc_data_structures/src/small_c_str/tests.rs b/compiler/rustc_data_structures/src/small_c_str/tests.rs
index 47277604b2b..7b975dadcb7 100644
--- a/compiler/rustc_data_structures/src/small_c_str/tests.rs
+++ b/compiler/rustc_data_structures/src/small_c_str/tests.rs
@@ -43,3 +43,11 @@ fn long() {
 fn internal_nul() {
     let _ = SmallCStr::new("abcd\0def");
 }
+
+#[test]
+fn from_cstr() {
+    let c = c"foo";
+    let s: SmallCStr = c.into();
+    assert_eq!(s.len_with_nul(), 4);
+    assert_eq!(s.as_c_str(), c"foo");
+}
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index e174cba7813..a1abe8fd4f3 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -376,7 +376,7 @@ impl From<Cow<'static, str>> for DiagnosticMessage {
     }
 }
 
-/// A workaround for good_path_delayed_bug ICEs when formatting types in disabled lints.
+/// A workaround for must_produce_diag ICEs when formatting types in disabled lints.
 ///
 /// Delays formatting until `.into(): DiagnosticMessage` is used.
 pub struct DelayDm<F>(pub F);
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 37f568f12a7..1a34a83c1a4 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -85,11 +85,7 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
 /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
 fn annotation_type_for_level(level: Level) -> AnnotationType {
     match level {
-        Level::Bug
-        | Level::Fatal
-        | Level::Error
-        | Level::DelayedBug
-        | Level::GoodPathDelayedBug => AnnotationType::Error,
+        Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => AnnotationType::Error,
         Level::ForceWarning(_) | Level::Warning => AnnotationType::Warning,
         Level::Note | Level::OnceNote => AnnotationType::Note,
         Level::Help | Level::OnceHelp => AnnotationType::Help,
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 2deb18484ec..b14a12175c7 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -237,8 +237,7 @@ impl Diagnostic {
         match self.level {
             Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
 
-            Level::GoodPathDelayedBug
-            | Level::ForceWarning(_)
+            Level::ForceWarning(_)
             | Level::Warning
             | Level::Note
             | Level::OnceNote
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index b9e92dbb31c..38c6661377b 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -1635,7 +1635,8 @@ impl HumanEmitter {
                     let mut to_add = FxHashMap::default();
 
                     for (depth, style) in depths {
-                        if multilines.remove(&depth).is_none() {
+                        // FIXME(#120456) - is `swap_remove` correct?
+                        if multilines.swap_remove(&depth).is_none() {
                             to_add.insert(depth, style);
                         }
                     }
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index da9ef6627be..d876f28040d 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -78,6 +78,7 @@ use std::fmt;
 use std::hash::Hash;
 use std::io::Write;
 use std::num::NonZeroUsize;
+use std::ops::DerefMut;
 use std::panic;
 use std::path::{Path, PathBuf};
 
@@ -435,7 +436,6 @@ struct DiagCtxtInner {
     lint_err_guars: Vec<ErrorGuaranteed>,
     /// The delayed bugs and their error guarantees.
     delayed_bugs: Vec<(DelayedDiagnostic, ErrorGuaranteed)>,
-    good_path_delayed_bugs: Vec<DelayedDiagnostic>,
 
     /// The number of stashed errors. Unlike the other counts, this can go up
     /// and down, so it doesn't guarantee anything.
@@ -446,13 +446,18 @@ struct DiagCtxtInner {
     /// The warning count shown to the user at the end.
     deduplicated_warn_count: usize,
 
+    emitter: Box<DynEmitter>,
+
+    /// Must we produce a diagnostic to justify the use of the expensive
+    /// `trimmed_def_paths` function?
+    must_produce_diag: bool,
+
     /// Has this diagnostic context printed any diagnostics? (I.e. has
     /// `self.emitter.emit_diagnostic()` been called?
     has_printed: bool,
 
-    emitter: Box<DynEmitter>,
     /// This flag indicates that an expected diagnostic was emitted and suppressed.
-    /// This is used for the `good_path_delayed_bugs` check.
+    /// This is used for the `must_produce_diag` check.
     suppressed_expected_diag: bool,
 
     /// This set contains the code of all emitted diagnostics to avoid
@@ -533,11 +538,6 @@ fn default_track_diagnostic(diag: Diagnostic, f: &mut dyn FnMut(Diagnostic)) {
 pub static TRACK_DIAGNOSTIC: AtomicRef<fn(Diagnostic, &mut dyn FnMut(Diagnostic))> =
     AtomicRef::new(&(default_track_diagnostic as _));
 
-enum DelayedBugKind {
-    Normal,
-    GoodPath,
-}
-
 #[derive(Copy, Clone, Default)]
 pub struct DiagCtxtFlags {
     /// If false, warning-level lints are suppressed.
@@ -563,11 +563,16 @@ impl Drop for DiagCtxtInner {
         self.emit_stashed_diagnostics();
 
         if self.err_guars.is_empty() {
-            self.flush_delayed(DelayedBugKind::Normal)
+            self.flush_delayed()
         }
 
         if !self.has_printed && !self.suppressed_expected_diag && !std::thread::panicking() {
-            self.flush_delayed(DelayedBugKind::GoodPath);
+            if self.must_produce_diag {
+                panic!(
+                    "must_produce_diag: trimmed_def_paths called but no diagnostics emitted; \
+                       use `DelayDm` for lints or `with_no_trimmed_paths` for debugging"
+                );
+            }
         }
 
         if self.check_unstable_expect_diagnostics {
@@ -609,12 +614,12 @@ impl DiagCtxt {
                 err_guars: Vec::new(),
                 lint_err_guars: Vec::new(),
                 delayed_bugs: Vec::new(),
-                good_path_delayed_bugs: Vec::new(),
                 stashed_err_count: 0,
                 deduplicated_err_count: 0,
                 deduplicated_warn_count: 0,
-                has_printed: false,
                 emitter,
+                must_produce_diag: false,
+                has_printed: false,
                 suppressed_expected_diag: false,
                 taught_diagnostics: Default::default(),
                 emitted_diagnostic_codes: Default::default(),
@@ -662,21 +667,51 @@ impl DiagCtxt {
     /// tools that want to reuse a `Parser` cleaning the previously emitted diagnostics as well as
     /// the overall count of emitted error diagnostics.
     pub fn reset_err_count(&self) {
+        // Use destructuring so that if a field gets added to `DiagCtxtInner`, it's impossible to
+        // fail to update this method as well.
         let mut inner = self.inner.borrow_mut();
-        inner.stashed_err_count = 0;
-        inner.deduplicated_err_count = 0;
-        inner.deduplicated_warn_count = 0;
-        inner.has_printed = false;
-
-        // actually free the underlying memory (which `clear` would not do)
-        inner.err_guars = Default::default();
-        inner.lint_err_guars = Default::default();
-        inner.delayed_bugs = Default::default();
-        inner.good_path_delayed_bugs = Default::default();
-        inner.taught_diagnostics = Default::default();
-        inner.emitted_diagnostic_codes = Default::default();
-        inner.emitted_diagnostics = Default::default();
-        inner.stashed_diagnostics = Default::default();
+        let DiagCtxtInner {
+            flags: _,
+            err_guars,
+            lint_err_guars,
+            delayed_bugs,
+            stashed_err_count,
+            deduplicated_err_count,
+            deduplicated_warn_count,
+            emitter: _,
+            must_produce_diag,
+            has_printed,
+            suppressed_expected_diag,
+            taught_diagnostics,
+            emitted_diagnostic_codes,
+            emitted_diagnostics,
+            stashed_diagnostics,
+            future_breakage_diagnostics,
+            check_unstable_expect_diagnostics,
+            unstable_expect_diagnostics,
+            fulfilled_expectations,
+            ice_file: _,
+        } = inner.deref_mut();
+
+        // For the `Vec`s and `HashMap`s, we overwrite with an empty container to free the
+        // underlying memory (which `clear` would not do).
+        *err_guars = Default::default();
+        *lint_err_guars = Default::default();
+        *delayed_bugs = Default::default();
+        *stashed_err_count = 0;
+        *deduplicated_err_count = 0;
+        *deduplicated_warn_count = 0;
+        *must_produce_diag = false;
+        *has_printed = false;
+        *suppressed_expected_diag = false;
+        *taught_diagnostics = Default::default();
+        *emitted_diagnostic_codes = Default::default();
+        *emitted_diagnostics = Default::default();
+        *stashed_diagnostics = Default::default();
+        *future_breakage_diagnostics = Default::default();
+        *check_unstable_expect_diagnostics = false;
+        *unstable_expect_diagnostics = Default::default();
+        *fulfilled_expectations = Default::default();
     }
 
     /// Stash a given diagnostic with the given `Span` and [`StashKey`] as the key.
@@ -702,7 +737,8 @@ impl DiagCtxt {
     pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
         let mut inner = self.inner.borrow_mut();
         let key = (span.with_parent(None), key);
-        let diag = inner.stashed_diagnostics.remove(&key)?;
+        // FIXME(#120456) - is `swap_remove` correct?
+        let diag = inner.stashed_diagnostics.swap_remove(&key)?;
         if diag.is_error() {
             if diag.is_lint.is_none() {
                 inner.stashed_err_count -= 1;
@@ -775,11 +811,12 @@ impl DiagCtxt {
         match (errors.len(), warnings.len()) {
             (0, 0) => return,
             (0, _) => {
-                // Use `inner.emitter` directly, otherwise the warning might not be emitted, e.g.
-                // with a configuration like `--cap-lints allow --force-warn bare_trait_objects`.
-                inner
-                    .emitter
-                    .emit_diagnostic(Diagnostic::new(Warning, DiagnosticMessage::Str(warnings)));
+                // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
+                // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
+                inner.emit_diagnostic(Diagnostic::new(
+                    ForceWarning(None),
+                    DiagnosticMessage::Str(warnings),
+                ));
             }
             (_, 0) => {
                 inner.emit_diagnostic(Diagnostic::new(Error, errors));
@@ -807,20 +844,23 @@ impl DiagCtxt {
                 error_codes.sort();
                 if error_codes.len() > 1 {
                     let limit = if error_codes.len() > 9 { 9 } else { error_codes.len() };
-                    inner.failure_note(format!(
+                    let msg1 = format!(
                         "Some errors have detailed explanations: {}{}",
                         error_codes[..limit].join(", "),
                         if error_codes.len() > 9 { "..." } else { "." }
-                    ));
-                    inner.failure_note(format!(
+                    );
+                    let msg2 = format!(
                         "For more information about an error, try `rustc --explain {}`.",
                         &error_codes[0]
-                    ));
+                    );
+                    inner.emit_diagnostic(Diagnostic::new(FailureNote, msg1));
+                    inner.emit_diagnostic(Diagnostic::new(FailureNote, msg2));
                 } else {
-                    inner.failure_note(format!(
+                    let msg = format!(
                         "For more information about this error, try `rustc --explain {}`.",
                         &error_codes[0]
-                    ));
+                    );
+                    inner.emit_diagnostic(Diagnostic::new(FailureNote, msg));
                 }
             }
         }
@@ -843,10 +883,6 @@ impl DiagCtxt {
         self.inner.borrow_mut().taught_diagnostics.insert(code)
     }
 
-    pub fn force_print_diagnostic(&self, db: Diagnostic) {
-        self.inner.borrow_mut().emitter.emit_diagnostic(db);
-    }
-
     pub fn emit_diagnostic(&self, diagnostic: Diagnostic) -> Option<ErrorGuaranteed> {
         self.inner.borrow_mut().emit_diagnostic(diagnostic)
     }
@@ -934,7 +970,13 @@ impl DiagCtxt {
     }
 
     pub fn flush_delayed(&self) {
-        self.inner.borrow_mut().flush_delayed(DelayedBugKind::Normal);
+        self.inner.borrow_mut().flush_delayed();
+    }
+
+    /// Used when trimmed_def_paths is called and we must produce a diagnostic
+    /// to justify its cost.
+    pub fn set_must_produce_diag(&self) {
+        self.inner.borrow_mut().must_produce_diag = true;
     }
 }
 
@@ -1108,13 +1150,6 @@ impl DiagCtxt {
         DiagnosticBuilder::<ErrorGuaranteed>::new(self, DelayedBug, msg).with_span(sp).emit()
     }
 
-    /// Ensures that a diagnostic is printed. See `Level::GoodPathDelayedBug`.
-    // No `#[rustc_lint_diagnostics]` because bug messages aren't user-facing.
-    #[track_caller]
-    pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
-        DiagnosticBuilder::<()>::new(self, GoodPathDelayedBug, msg).emit()
-    }
-
     #[rustc_lint_diagnostics]
     #[track_caller]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
@@ -1175,7 +1210,7 @@ impl DiagCtxt {
         span: impl Into<MultiSpan>,
         msg: impl Into<DiagnosticMessage>,
     ) -> DiagnosticBuilder<'_, ()> {
-        DiagnosticBuilder::new(self, Note, msg).with_span(span)
+        self.struct_note(msg).with_span(span)
     }
 
     #[rustc_lint_diagnostics]
@@ -1205,6 +1240,15 @@ impl DiagCtxt {
 
     #[rustc_lint_diagnostics]
     #[track_caller]
+    pub fn struct_failure_note(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+    ) -> DiagnosticBuilder<'_, ()> {
+        DiagnosticBuilder::new(self, FailureNote, msg)
+    }
+
+    #[rustc_lint_diagnostics]
+    #[track_caller]
     pub fn struct_allow(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Allow, msg)
     }
@@ -1266,42 +1310,39 @@ impl DiagCtxtInner {
         if diagnostic.has_future_breakage() {
             // Future breakages aren't emitted if they're Level::Allow,
             // but they still need to be constructed and stashed below,
-            // so they'll trigger the good-path bug check.
+            // so they'll trigger the must_produce_diag check.
             self.suppressed_expected_diag = true;
             self.future_breakage_diagnostics.push(diagnostic.clone());
         }
 
-        if matches!(diagnostic.level, DelayedBug | GoodPathDelayedBug)
-            && self.flags.eagerly_emit_delayed_bugs
-        {
+        // Note that because this comes before the `match` below,
+        // `-Zeagerly-emit-delayed-bugs` continues to work even after we've
+        // issued an error and stopped recording new delayed bugs.
+        if diagnostic.level == DelayedBug && self.flags.eagerly_emit_delayed_bugs {
             diagnostic.level = Error;
         }
 
         match diagnostic.level {
-            // This must come after the possible promotion of `DelayedBug`/`GoodPathDelayedBug` to
+            // This must come after the possible promotion of `DelayedBug` to
             // `Error` above.
             Fatal | Error if self.treat_next_err_as_bug() => {
                 diagnostic.level = Bug;
             }
             DelayedBug => {
-                // FIXME(eddyb) this should check for `has_errors` and stop pushing
-                // once *any* errors were emitted (and truncate `delayed_bugs`
-                // when an error is first emitted, also), but maybe there's a case
-                // in which that's not sound? otherwise this is really inefficient.
-                let backtrace = std::backtrace::Backtrace::capture();
-                // This `unchecked_error_guaranteed` is valid. It is where the
-                // `ErrorGuaranteed` for delayed bugs originates.
-                #[allow(deprecated)]
-                let guar = ErrorGuaranteed::unchecked_error_guaranteed();
-                self.delayed_bugs
-                    .push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar));
-                return Some(guar);
-            }
-            GoodPathDelayedBug => {
-                let backtrace = std::backtrace::Backtrace::capture();
-                self.good_path_delayed_bugs
-                    .push(DelayedDiagnostic::with_backtrace(diagnostic, backtrace));
-                return None;
+                // 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() {
+                    Some(guar)
+                } else {
+                    let backtrace = std::backtrace::Backtrace::capture();
+                    // This `unchecked_error_guaranteed` is valid. It is where the
+                    // `ErrorGuaranteed` for delayed bugs originates.
+                    #[allow(deprecated)]
+                    let guar = ErrorGuaranteed::unchecked_error_guaranteed();
+                    self.delayed_bugs
+                        .push((DelayedDiagnostic::with_backtrace(diagnostic, backtrace), guar));
+                    Some(guar)
+                };
             }
             Warning if !self.flags.can_emit_warnings => {
                 if diagnostic.has_future_breakage() {
@@ -1367,6 +1408,16 @@ impl DiagCtxtInner {
             }
 
             if is_error {
+                // If we have any delayed bugs recorded, we can discard them
+                // because they won't be used. (This should only occur if there
+                // have been no errors previously emitted, because we don't add
+                // new delayed bugs once the first error is emitted.)
+                if !self.delayed_bugs.is_empty() {
+                    assert_eq!(self.lint_err_guars.len() + self.err_guars.len(), 0);
+                    self.delayed_bugs.clear();
+                    self.delayed_bugs.shrink_to_fit();
+                }
+
                 // This `unchecked_error_guaranteed` is valid. It is where the
                 // `ErrorGuaranteed` for errors and lint errors originates.
                 #[allow(deprecated)]
@@ -1410,27 +1461,14 @@ impl DiagCtxtInner {
             .or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied())
     }
 
-    fn failure_note(&mut self, msg: impl Into<DiagnosticMessage>) {
-        self.emit_diagnostic(Diagnostic::new(FailureNote, msg));
-    }
-
-    fn flush_delayed(&mut self, kind: DelayedBugKind) {
-        let (bugs, note1) = match kind {
-            DelayedBugKind::Normal => (
-                std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect(),
-                "no errors encountered even though delayed bugs were created",
-            ),
-            DelayedBugKind::GoodPath => (
-                std::mem::take(&mut self.good_path_delayed_bugs),
-                "no warnings or errors encountered even though good path delayed bugs were created",
-            ),
-        };
-        let note2 = "those delayed bugs will now be shown as internal compiler errors";
-
-        if bugs.is_empty() {
+    fn flush_delayed(&mut self) {
+        if self.delayed_bugs.is_empty() {
             return;
         }
 
+        let bugs: Vec<_> =
+            std::mem::take(&mut self.delayed_bugs).into_iter().map(|(b, _)| b).collect();
+
         // If backtraces are enabled, also print the query stack
         let backtrace = std::env::var_os("RUST_BACKTRACE").map_or(true, |x| &x != "0");
         for (i, bug) in bugs.into_iter().enumerate() {
@@ -1454,6 +1492,8 @@ impl DiagCtxtInner {
                 // frame them better (e.g. separate warnings from them). Also,
                 // make it a note so it doesn't count as an error, because that
                 // could trigger `-Ztreat-err-as-bug`, which we don't want.
+                let note1 = "no errors encountered even though delayed bugs were created";
+                let note2 = "those delayed bugs will now be shown as internal compiler errors";
                 self.emit_diagnostic(Diagnostic::new(Note, note1));
                 self.emit_diagnostic(Diagnostic::new(Note, note2));
             }
@@ -1462,7 +1502,7 @@ impl DiagCtxtInner {
                 if backtrace || self.ice_file.is_none() { bug.decorate() } else { bug.inner };
 
             // "Undelay" the delayed bugs (into plain `Bug`s).
-            if !matches!(bug.level, DelayedBug | GoodPathDelayedBug) {
+            if bug.level != DelayedBug {
                 // NOTE(eddyb) not panicking here because we're already producing
                 // an ICE, and the more information the merrier.
                 bug.subdiagnostic(InvalidFlushedDelayedDiagnosticLevel {
@@ -1534,7 +1574,6 @@ impl DelayedDiagnostic {
 /// Fatal              yes       FatalAbort/FatalError(*)  yes        -     -
 /// Error              yes       ErrorGuaranteed           yes        -     yes
 /// DelayedBug         yes       ErrorGuaranteed           yes        -     -
-/// GoodPathDelayedBug -         ()                        yes        -     -
 /// ForceWarning       -         ()                        yes        -     lint-only
 /// Warning            -         ()                        yes        yes   yes
 /// Note               -         ()                        rare       yes   -
@@ -1567,20 +1606,6 @@ pub enum Level {
     /// that should only be reached when compiling erroneous code.
     DelayedBug,
 
-    /// Like `DelayedBug`, but weaker: lets you register an error without emitting it. If
-    /// compilation ends without any other diagnostics being emitted (and without an expected lint
-    /// being suppressed), this will be emitted as a bug. Otherwise, it will be silently dropped.
-    /// I.e. "expect other diagnostics are emitted (or suppressed)" semantics. Useful on code paths
-    /// that should only be reached when emitting diagnostics, e.g. for expensive one-time
-    /// diagnostic formatting operations.
-    ///
-    /// FIXME(nnethercote) good path delayed bugs are semantically strange: if printed they produce
-    /// an ICE, but they don't satisfy `is_error` and they don't guarantee an error is emitted.
-    /// Plus there's the extra complication with expected (suppressed) lints. They have limited
-    /// use, and are used in very few places, and "good path" isn't a good name. It would be good
-    /// to remove them.
-    GoodPathDelayedBug,
-
     /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
     /// from finishing.
     ///
@@ -1625,7 +1650,7 @@ impl Level {
     fn color(self) -> ColorSpec {
         let mut spec = ColorSpec::new();
         match self {
-            Bug | Fatal | Error | DelayedBug | GoodPathDelayedBug => {
+            Bug | Fatal | Error | DelayedBug => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
             ForceWarning(_) | Warning => {
@@ -1645,7 +1670,7 @@ impl Level {
 
     pub fn to_str(self) -> &'static str {
         match self {
-            Bug | DelayedBug | GoodPathDelayedBug => "error: internal compiler error",
+            Bug | DelayedBug => "error: internal compiler error",
             Fatal | Error => "error",
             ForceWarning(_) | Warning => "warning",
             Note | OnceNote => "note",
@@ -1670,8 +1695,8 @@ impl Level {
     // subdiagnostic message?
     fn can_be_top_or_sub(&self) -> (bool, bool) {
         match self {
-            Bug | DelayedBug | Fatal | Error | GoodPathDelayedBug | ForceWarning(_)
-            | FailureNote | Allow | Expect(_) => (true, false),
+            Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow
+            | Expect(_) => (true, false),
 
             Warning | Note | Help => (true, true),
 
diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs
index eec86c36aed..be4b6399e12 100644
--- a/compiler/rustc_expand/src/mbe/diagnostics.rs
+++ b/compiler/rustc_expand/src/mbe/diagnostics.rs
@@ -34,10 +34,10 @@ pub(super) fn failed_to_match_macro<'cx>(
     if try_success_result.is_ok() {
         // Nonterminal parser recovery might turn failed matches into successful ones,
         // but for that it must have emitted an error already
-        tracker
-            .cx
-            .dcx()
-            .span_delayed_bug(sp, "Macro matching returned a success on the second try");
+        assert!(
+            tracker.cx.dcx().has_errors().is_some(),
+            "Macro matching returned a success on the second try"
+        );
     }
 
     if let Some(result) = tracker.result {
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index a001044c3e5..98a27c5ed20 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -758,8 +758,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             // since we should have emitten an error for them earlier, and they will
             // not be well-formed!
             if polarity == ty::ImplPolarity::Negative {
-                self.tcx().dcx().span_delayed_bug(
-                    binding.span,
+                assert!(
+                    self.tcx().dcx().has_errors().is_some(),
                     "negative trait bounds should not have bindings",
                 );
                 continue;
diff --git a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
index cbbf560076e..7705445ffaa 100644
--- a/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/object_safety.rs
@@ -218,7 +218,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         for def_ids in associated_types.values_mut() {
             for (projection_bound, span) in &projection_bounds {
                 let def_id = projection_bound.projection_def_id();
-                def_ids.remove(&def_id);
+                // FIXME(#120456) - is `swap_remove` correct?
+                def_ids.swap_remove(&def_id);
                 if tcx.generics_require_sized_self(def_id) {
                     tcx.emit_node_span_lint(
                         UNUSED_ASSOCIATED_TYPE_BOUNDS,
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 7f674a1e7e4..e7506cee60e 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1865,13 +1865,13 @@ fn check_variances_for_type_defn<'tcx>(
         let hir_param = &hir_generics.params[index];
 
         if ty_param.def_id != hir_param.def_id.into() {
-            // valid programs always have lifetimes before types in the generic parameter list
+            // Valid programs always have lifetimes before types in the generic parameter list.
             // ty_generics are normalized to be in this required order, and variances are built
             // from ty generics, not from hir generics. but we need hir generics to get
-            // a span out
+            // a span out.
             //
-            // if they aren't in the same order, then the user has written invalid code, and already
-            // got an error about it (or I'm wrong about this)
+            // If they aren't in the same order, then the user has written invalid code, and already
+            // got an error about it (or I'm wrong about this).
             tcx.dcx().span_delayed_bug(
                 hir_param.span,
                 "hir generics and ty generics in different order",
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index 370c6c607d7..6c3a9b747ef 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -15,7 +15,7 @@ use rustc_infer::infer::{DefineOpaqueTypes, TyCtxtInferExt};
 use rustc_infer::traits::Obligation;
 use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
 use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitableExt};
-use rustc_span::Span;
+use rustc_span::{Span, DUMMY_SP};
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::misc::{
     type_allowed_to_implement_const_param_ty, type_allowed_to_implement_copy,
@@ -25,13 +25,14 @@ use rustc_trait_selection::traits::ObligationCtxt;
 use rustc_trait_selection::traits::{self, ObligationCause};
 use std::collections::BTreeMap;
 
-pub fn check_trait(
-    tcx: TyCtxt<'_>,
+pub fn check_trait<'tcx>(
+    tcx: TyCtxt<'tcx>,
     trait_def_id: DefId,
     impl_def_id: LocalDefId,
+    impl_header: ty::ImplTraitHeader<'tcx>,
 ) -> Result<(), ErrorGuaranteed> {
     let lang_items = tcx.lang_items();
-    let checker = Checker { tcx, trait_def_id, impl_def_id };
+    let checker = Checker { tcx, trait_def_id, impl_def_id, impl_header };
     let mut res = checker.check(lang_items.drop_trait(), visit_implementation_of_drop);
     res = res.and(checker.check(lang_items.copy_trait(), visit_implementation_of_copy));
     res = res.and(
@@ -50,24 +51,25 @@ struct Checker<'tcx> {
     tcx: TyCtxt<'tcx>,
     trait_def_id: DefId,
     impl_def_id: LocalDefId,
+    impl_header: ty::ImplTraitHeader<'tcx>,
 }
 
 impl<'tcx> Checker<'tcx> {
     fn check(
         &self,
         trait_def_id: Option<DefId>,
-        f: impl FnOnce(TyCtxt<'tcx>, LocalDefId) -> Result<(), ErrorGuaranteed>,
+        f: impl FnOnce(&Self) -> Result<(), ErrorGuaranteed>,
     ) -> Result<(), ErrorGuaranteed> {
-        if Some(self.trait_def_id) == trait_def_id { f(self.tcx, self.impl_def_id) } else { Ok(()) }
+        if Some(self.trait_def_id) == trait_def_id { f(self) } else { Ok(()) }
     }
 }
 
-fn visit_implementation_of_drop(
-    tcx: TyCtxt<'_>,
-    impl_did: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
+fn visit_implementation_of_drop(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let header = checker.impl_header;
+    let impl_did = checker.impl_def_id;
     // Destructors only work on local ADT types.
-    match tcx.type_of(impl_did).instantiate_identity().kind() {
+    match header.trait_ref.self_ty().kind() {
         ty::Adt(def, _) if def.did().is_local() => return Ok(()),
         ty::Error(_) => return Ok(()),
         _ => {}
@@ -78,13 +80,13 @@ fn visit_implementation_of_drop(
     Err(tcx.dcx().emit_err(errors::DropImplOnWrongItem { span: impl_.self_ty.span }))
 }
 
-fn visit_implementation_of_copy(
-    tcx: TyCtxt<'_>,
-    impl_did: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
+fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let impl_header = checker.impl_header;
+    let impl_did = checker.impl_def_id;
     debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
 
-    let self_type = tcx.type_of(impl_did).instantiate_identity();
+    let self_type = impl_header.trait_ref.self_ty();
     debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
 
     let param_env = tcx.param_env(impl_did);
@@ -92,56 +94,58 @@ fn visit_implementation_of_copy(
 
     debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
 
-    if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) {
+    if let ty::ImplPolarity::Negative = impl_header.polarity {
         return Ok(());
     }
-    let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
 
-    let cause = traits::ObligationCause::misc(span, impl_did);
+    let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
     match type_allowed_to_implement_copy(tcx, param_env, self_type, cause) {
         Ok(()) => Ok(()),
         Err(CopyImplementationError::InfringingFields(fields)) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(infringing_fields_error(tcx, fields, LangItem::Copy, impl_did, span))
         }
         Err(CopyImplementationError::NotAnAdt) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(tcx.dcx().emit_err(errors::CopyImplOnNonAdt { span }))
         }
         Err(CopyImplementationError::HasDestructor) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(tcx.dcx().emit_err(errors::CopyImplOnTypeWithDtor { span }))
         }
     }
 }
 
-fn visit_implementation_of_const_param_ty(
-    tcx: TyCtxt<'_>,
-    impl_did: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
-    let self_type = tcx.type_of(impl_did).instantiate_identity();
+fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let header = checker.impl_header;
+    let impl_did = checker.impl_def_id;
+    let self_type = header.trait_ref.self_ty();
     assert!(!self_type.has_escaping_bound_vars());
 
     let param_env = tcx.param_env(impl_did);
 
-    if let ty::ImplPolarity::Negative = tcx.impl_polarity(impl_did) {
+    if let ty::ImplPolarity::Negative = header.polarity {
         return Ok(());
     }
-    let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
 
-    let cause = traits::ObligationCause::misc(span, impl_did);
+    let cause = traits::ObligationCause::misc(DUMMY_SP, impl_did);
     match type_allowed_to_implement_const_param_ty(tcx, param_env, self_type, cause) {
         Ok(()) => Ok(()),
         Err(ConstParamTyImplementationError::InfrigingFields(fields)) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(infringing_fields_error(tcx, fields, LangItem::ConstParamTy, impl_did, span))
         }
         Err(ConstParamTyImplementationError::NotAnAdtOrBuiltinAllowed) => {
+            let span = tcx.hir().expect_item(impl_did).expect_impl().self_ty.span;
             Err(tcx.dcx().emit_err(errors::ConstParamTyImplOnNonAdt { span }))
         }
     }
 }
 
-fn visit_implementation_of_coerce_unsized(
-    tcx: TyCtxt<'_>,
-    impl_did: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
+fn visit_implementation_of_coerce_unsized(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let impl_did = checker.impl_def_id;
     debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
 
     // Just compute this for the side-effects, in particular reporting
@@ -151,20 +155,20 @@ fn visit_implementation_of_coerce_unsized(
     tcx.at(span).ensure().coerce_unsized_info(impl_did)
 }
 
-fn visit_implementation_of_dispatch_from_dyn(
-    tcx: TyCtxt<'_>,
-    impl_did: LocalDefId,
-) -> Result<(), ErrorGuaranteed> {
+fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> {
+    let tcx = checker.tcx;
+    let header = checker.impl_header;
+    let impl_did = checker.impl_def_id;
+    let trait_ref = header.trait_ref;
     debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
 
     let span = tcx.def_span(impl_did);
 
     let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
 
-    let source = tcx.type_of(impl_did).instantiate_identity();
+    let source = trait_ref.self_ty();
     assert!(!source.has_escaping_bound_vars());
     let target = {
-        let trait_ref = tcx.impl_trait_ref(impl_did).unwrap().instantiate_identity();
         assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
 
         trait_ref.args.type_at(1)
diff --git a/compiler/rustc_hir_analysis/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index 7f59763f2a0..d6281fa08f7 100644
--- a/compiler/rustc_hir_analysis/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
@@ -23,6 +23,7 @@ fn check_impl(
     tcx: TyCtxt<'_>,
     impl_def_id: LocalDefId,
     trait_ref: ty::TraitRef<'_>,
+    trait_def: &ty::TraitDef,
 ) -> Result<(), ErrorGuaranteed> {
     debug!(
         "(checking implementation) adding impl for trait '{:?}', item '{}'",
@@ -36,19 +37,20 @@ fn check_impl(
         return Ok(());
     }
 
-    enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id)
-        .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id))
+    enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id, trait_def)
+        .and(enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id, trait_def))
 }
 
 fn enforce_trait_manually_implementable(
     tcx: TyCtxt<'_>,
     impl_def_id: LocalDefId,
     trait_def_id: DefId,
+    trait_def: &ty::TraitDef,
 ) -> Result<(), ErrorGuaranteed> {
     let impl_header_span = tcx.def_span(impl_def_id);
 
     // Disallow *all* explicit impls of traits marked `#[rustc_deny_explicit_impl]`
-    if tcx.trait_def(trait_def_id).deny_explicit_impl {
+    if trait_def.deny_explicit_impl {
         let trait_name = tcx.item_name(trait_def_id);
         let mut err = struct_span_code_err!(
             tcx.dcx(),
@@ -67,8 +69,7 @@ fn enforce_trait_manually_implementable(
         return Err(err.emit());
     }
 
-    if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable =
-        tcx.trait_def(trait_def_id).specialization_kind
+    if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = trait_def.specialization_kind
     {
         if !tcx.features().specialization
             && !tcx.features().min_specialization
@@ -87,8 +88,9 @@ fn enforce_empty_impls_for_marker_traits(
     tcx: TyCtxt<'_>,
     impl_def_id: LocalDefId,
     trait_def_id: DefId,
+    trait_def: &ty::TraitDef,
 ) -> Result<(), ErrorGuaranteed> {
-    if !tcx.trait_def(trait_def_id).is_marker {
+    if !trait_def.is_marker {
         return Ok(());
     }
 
@@ -132,14 +134,15 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) -> Result<(), ErrorGuaranteed>
     let mut res = tcx.ensure().specialization_graph_of(def_id);
 
     for &impl_def_id in impls {
-        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().instantiate_identity();
+        let trait_header = tcx.impl_trait_header(impl_def_id).unwrap().instantiate_identity();
+        let trait_def = tcx.trait_def(trait_header.trait_ref.def_id);
 
-        res = res.and(check_impl(tcx, impl_def_id, trait_ref));
-        res = res.and(check_object_overlap(tcx, impl_def_id, trait_ref));
+        res = res.and(check_impl(tcx, impl_def_id, trait_header.trait_ref, trait_def));
+        res = res.and(check_object_overlap(tcx, impl_def_id, trait_header.trait_ref));
 
-        res = res.and(unsafety::check_item(tcx, impl_def_id, trait_ref));
+        res = res.and(unsafety::check_item(tcx, impl_def_id, trait_header, trait_def));
         res = res.and(tcx.ensure().orphan_check_impl(impl_def_id));
-        res = res.and(builtin::check_trait(tcx, def_id, impl_def_id));
+        res = res.and(builtin::check_trait(tcx, def_id, impl_def_id, trait_header));
     }
 
     res
diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
index d217d53587d..53a5ada4105 100644
--- a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
@@ -2,23 +2,23 @@
 //! crate or pertains to a type defined in this crate.
 
 use rustc_errors::{codes::*, struct_span_code_err};
-use rustc_hir as hir;
 use rustc_hir::Unsafety;
-use rustc_middle::ty::{TraitRef, TyCtxt};
+use rustc_middle::ty::{ImplPolarity::*, ImplTraitHeader, TraitDef, TyCtxt};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::ErrorGuaranteed;
 
 pub(super) fn check_item(
     tcx: TyCtxt<'_>,
     def_id: LocalDefId,
-    trait_ref: TraitRef<'_>,
+    trait_header: ImplTraitHeader<'_>,
+    trait_def: &TraitDef,
 ) -> Result<(), ErrorGuaranteed> {
-    let item = tcx.hir().expect_item(def_id);
-    let impl_ = item.expect_impl();
-    let trait_def = tcx.trait_def(trait_ref.def_id);
-    let unsafe_attr = impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
-    match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
-        (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
+    let trait_ref = trait_header.trait_ref;
+    let unsafe_attr =
+        tcx.generics_of(def_id).params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
+    match (trait_def.unsafety, unsafe_attr, trait_header.unsafety, trait_header.polarity) {
+        (Unsafety::Normal, None, Unsafety::Unsafe, Positive | Reservation) => {
+            let span = tcx.def_span(def_id);
             return Err(struct_span_code_err!(
                 tcx.dcx(),
                 tcx.def_span(def_id),
@@ -27,7 +27,7 @@ pub(super) fn check_item(
                 trait_ref.print_trait_sugared()
             )
             .with_span_suggestion_verbose(
-                item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)),
+                span.with_hi(span.lo() + rustc_span::BytePos(7)),
                 "remove `unsafe` from this trait implementation",
                 "",
                 rustc_errors::Applicability::MachineApplicable,
@@ -35,10 +35,11 @@ pub(super) fn check_item(
             .emit());
         }
 
-        (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
+        (Unsafety::Unsafe, _, Unsafety::Normal, Positive | Reservation) => {
+            let span = tcx.def_span(def_id);
             return Err(struct_span_code_err!(
                 tcx.dcx(),
-                tcx.def_span(def_id),
+                span,
                 E0200,
                 "the trait `{}` requires an `unsafe impl` declaration",
                 trait_ref.print_trait_sugared()
@@ -50,7 +51,7 @@ pub(super) fn check_item(
                 trait_ref.print_trait_sugared()
             ))
             .with_span_suggestion_verbose(
-                item.span.shrink_to_lo(),
+                span.shrink_to_lo(),
                 "add `unsafe` to this trait implementation",
                 "unsafe ",
                 rustc_errors::Applicability::MaybeIncorrect,
@@ -58,10 +59,11 @@ pub(super) fn check_item(
             .emit());
         }
 
-        (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
+        (Unsafety::Normal, Some(attr_name), Unsafety::Normal, Positive | Reservation) => {
+            let span = tcx.def_span(def_id);
             return Err(struct_span_code_err!(
                 tcx.dcx(),
-                tcx.def_span(def_id),
+                span,
                 E0569,
                 "requires an `unsafe impl` declaration due to `#[{}]` attribute",
                 attr_name
@@ -73,7 +75,7 @@ pub(super) fn check_item(
                 trait_ref.print_trait_sugared()
             ))
             .with_span_suggestion_verbose(
-                item.span.shrink_to_lo(),
+                span.shrink_to_lo(),
                 "add `unsafe` to this trait implementation",
                 "unsafe ",
                 rustc_errors::Applicability::MaybeIncorrect,
@@ -81,14 +83,14 @@ pub(super) fn check_item(
             .emit());
         }
 
-        (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
+        (_, _, Unsafety::Unsafe, Negative) => {
             // Reported in AST validation
-            tcx.dcx().span_delayed_bug(item.span, "unsafe negative impl");
+            assert!(tcx.dcx().has_errors().is_some(), "unsafe negative impl");
             Ok(())
         }
-        (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
-        | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
-        | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
+        (_, _, Unsafety::Normal, Negative)
+        | (Unsafety::Unsafe, _, Unsafety::Unsafe, Positive | Reservation)
+        | (Unsafety::Normal, Some(_), Unsafety::Unsafe, Positive | Reservation)
         | (Unsafety::Normal, None, Unsafety::Normal, _) => Ok(()),
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index e8787d159ae..4891dae47c6 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -1539,6 +1539,7 @@ fn impl_trait_header(
             };
             ty::EarlyBinder::bind(ty::ImplTraitHeader {
                 trait_ref,
+                unsafety: impl_.unsafety,
                 polarity: polarity_of_impl(tcx, def_id,  impl_, item.span)
             })
         })
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 c9cf43ddfc8..287cb880908 100644
--- a/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
+++ b/compiler/rustc_hir_analysis/src/collect/resolve_bound_vars.rs
@@ -1873,7 +1873,8 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
         lifetime_ref: &'tcx hir::Lifetime,
         bad_def: ResolvedArg,
     ) {
-        let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
+        // FIXME(#120456) - is `swap_remove` correct?
+        let old_value = self.map.defs.swap_remove(&lifetime_ref.hir_id);
         assert_eq!(old_value, Some(bad_def));
     }
 }
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index 1aaefc5b520..1cd77050217 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -187,8 +187,10 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
     }
 
     tcx.sess.time("wf_checking", || {
-        tcx.hir().try_par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
-    })?;
+        tcx.hir().par_for_each_module(|module| {
+            let _ = tcx.ensure().check_mod_type_wf(module);
+        })
+    });
 
     if tcx.features().rustc_attrs {
         collect::test_opaque_hidden_types(tcx)?;
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index f21de1609cb..9a8f287ec14 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -139,10 +139,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             | ty::Never
             | ty::Dynamic(_, _, ty::DynStar)
             | ty::Error(_) => {
-                let reported = self
+                let guar = self
                     .dcx()
                     .span_delayed_bug(span, format!("`{t:?}` should be sized but is not?"));
-                return Err(reported);
+                return Err(guar);
             }
         })
     }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index e98ace948f9..e6319747e78 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -98,8 +98,11 @@ struct CollectRetsVisitor<'tcx> {
 
 impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        if let hir::ExprKind::Ret(_) = expr.kind {
-            self.ret_exprs.push(expr);
+        match expr.kind {
+            hir::ExprKind::Ret(_) => self.ret_exprs.push(expr),
+            // `return` in closures does not return from the outer function
+            hir::ExprKind::Closure(_) => return,
+            _ => {}
         }
         intravisit::walk_expr(self, expr);
     }
@@ -1845,13 +1848,31 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         }
 
         let parent_id = fcx.tcx.hir().get_parent_item(id);
-        let parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
+        let mut parent_item = fcx.tcx.hir_node_by_def_id(parent_id.def_id);
+        // When suggesting return, we need to account for closures and async blocks, not just items.
+        for (_, node) in fcx.tcx.hir().parent_iter(id) {
+            match node {
+                hir::Node::Expr(&hir::Expr {
+                    kind: hir::ExprKind::Closure(hir::Closure { .. }),
+                    ..
+                }) => {
+                    parent_item = node;
+                    break;
+                }
+                hir::Node::Item(_) | hir::Node::TraitItem(_) | hir::Node::ImplItem(_) => break,
+                _ => {}
+            }
+        }
 
-        if let (Some(expr), Some(_), Some((fn_id, fn_decl, _, _))) =
-            (expression, blk_id, fcx.get_node_fn_decl(parent_item))
-        {
+        if let (Some(expr), Some(_), Some(fn_decl)) = (expression, blk_id, parent_item.fn_decl()) {
             fcx.suggest_missing_break_or_return_expr(
-                &mut err, expr, fn_decl, expected, found, id, fn_id,
+                &mut err,
+                expr,
+                fn_decl,
+                expected,
+                found,
+                id,
+                parent_id.into(),
             );
         }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index f7ff9f8224a..ce8b62d19b4 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -963,14 +963,35 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 owner_id,
                 ..
             }) => Some((hir::HirId::make_owner(owner_id.def_id), &sig.decl, ident, false)),
-            Node::Expr(&hir::Expr { hir_id, kind: hir::ExprKind::Closure(..), .. })
-                if let Node::Item(&hir::Item {
-                    ident,
-                    kind: hir::ItemKind::Fn(ref sig, ..),
-                    owner_id,
-                    ..
-                }) = self.tcx.parent_hir_node(hir_id) =>
-            {
+            Node::Expr(&hir::Expr {
+                hir_id,
+                kind:
+                    hir::ExprKind::Closure(hir::Closure {
+                        kind: hir::ClosureKind::Coroutine(..), ..
+                    }),
+                ..
+            }) => {
+                let (ident, sig, owner_id) = match self.tcx.parent_hir_node(hir_id) {
+                    Node::Item(&hir::Item {
+                        ident,
+                        kind: hir::ItemKind::Fn(ref sig, ..),
+                        owner_id,
+                        ..
+                    }) => (ident, sig, owner_id),
+                    Node::TraitItem(&hir::TraitItem {
+                        ident,
+                        kind: hir::TraitItemKind::Fn(ref sig, ..),
+                        owner_id,
+                        ..
+                    }) => (ident, sig, owner_id),
+                    Node::ImplItem(&hir::ImplItem {
+                        ident,
+                        kind: hir::ImplItemKind::Fn(ref sig, ..),
+                        owner_id,
+                        ..
+                    }) => (ident, sig, owner_id),
+                    _ => return None,
+                };
                 Some((
                     hir::HirId::make_owner(owner_id.def_id),
                     &sig.decl,
@@ -1558,7 +1579,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let ctxt = {
             let mut enclosing_breakables = self.enclosing_breakables.borrow_mut();
             debug_assert!(enclosing_breakables.stack.len() == index + 1);
-            enclosing_breakables.by_id.remove(&id).expect("missing breakable context");
+            // FIXME(#120456) - is `swap_remove` correct?
+            enclosing_breakables.by_id.swap_remove(&id).expect("missing breakable context");
             enclosing_breakables.stack.pop().expect("missing breakable context")
         };
         (ctxt, result)
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 35b3f27d791..65b8505c090 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -1726,7 +1726,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise.
-    fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
+    pub(crate) fn get_parent_fn_decl(
+        &self,
+        blk_id: hir::HirId,
+    ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> {
         let parent = self.tcx.hir_node_by_def_id(self.tcx.hir().get_parent_item(blk_id).def_id);
         self.get_node_fn_decl(parent).map(|(_, fn_decl, ident, _)| (fn_decl, ident))
     }
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
index d68e20541ef..8b6f263b1a7 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs
@@ -827,6 +827,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
             hir::FnRetTy::Return(hir_ty) => {
                 if let hir::TyKind::OpaqueDef(item_id, ..) = hir_ty.kind
+                    // FIXME: account for RPITIT.
                     && let hir::Node::Item(hir::Item {
                         kind: hir::ItemKind::OpaqueTy(op_ty), ..
                     }) = self.tcx.hir_node(item_id.hir_id())
@@ -1038,33 +1039,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
-        if let hir::FnRetTy::Return(ty) = fn_decl.output {
-            let ty = self.astconv().ast_ty_to_ty(ty);
-            let bound_vars = self.tcx.late_bound_vars(fn_id);
-            let ty = self
-                .tcx
-                .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
-            let ty = match self.tcx.asyncness(fn_id.owner) {
-                ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
-                    span_bug!(fn_decl.output.span(), "failed to get output type of async function")
-                }),
-                ty::Asyncness::No => ty,
-            };
-            let ty = self.normalize(expr.span, ty);
-            if self.can_coerce(found, ty) {
-                if let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
-                    && let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
-                {
-                    err.multipart_suggestion(
-                        "you might have meant to return this value",
-                        vec![
-                            (span.shrink_to_lo(), "return ".to_string()),
-                            (span.shrink_to_hi(), ";".to_string()),
-                        ],
-                        Applicability::MaybeIncorrect,
-                    );
-                }
+        let scope = self
+            .tcx
+            .hir()
+            .parent_iter(id)
+            .filter(|(_, node)| {
+                matches!(
+                    node,
+                    Node::Expr(Expr { kind: ExprKind::Closure(..), .. })
+                        | Node::Item(_)
+                        | Node::TraitItem(_)
+                        | Node::ImplItem(_)
+                )
+            })
+            .next();
+        let in_closure =
+            matches!(scope, Some((_, Node::Expr(Expr { kind: ExprKind::Closure(..), .. }))));
+
+        let can_return = match fn_decl.output {
+            hir::FnRetTy::Return(ty) => {
+                let ty = self.astconv().ast_ty_to_ty(ty);
+                let bound_vars = self.tcx.late_bound_vars(fn_id);
+                let ty = self
+                    .tcx
+                    .instantiate_bound_regions_with_erased(Binder::bind_with_vars(ty, bound_vars));
+                let ty = match self.tcx.asyncness(fn_id.owner) {
+                    ty::Asyncness::Yes => self.get_impl_future_output_ty(ty).unwrap_or_else(|| {
+                        span_bug!(
+                            fn_decl.output.span(),
+                            "failed to get output type of async function"
+                        )
+                    }),
+                    ty::Asyncness::No => ty,
+                };
+                let ty = self.normalize(expr.span, ty);
+                self.can_coerce(found, ty)
+            }
+            hir::FnRetTy::DefaultReturn(_) if in_closure => {
+                self.ret_coercion.as_ref().map_or(false, |ret| {
+                    let ret_ty = ret.borrow().expected_ty();
+                    self.can_coerce(found, ret_ty)
+                })
             }
+            _ => false,
+        };
+        if can_return
+            && let Some(owner_node) = self.tcx.hir_node(fn_id).as_owner()
+            && let Some(span) = expr.span.find_ancestor_inside(*owner_node.span())
+        {
+            err.multipart_suggestion(
+                "you might have meant to return this value",
+                vec![
+                    (span.shrink_to_lo(), "return ".to_string()),
+                    (span.shrink_to_hi(), ";".to_string()),
+                ],
+                Applicability::MaybeIncorrect,
+            );
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index c1af4b5983e..629c2f2a971 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -304,6 +304,10 @@ fn typeck_with_fallback<'tcx>(
 
     let typeck_results = fcx.resolve_type_vars_in_body(body);
 
+    // We clone the defined opaque types during writeback in the new solver
+    // because we have to use them during normalization.
+    let _ = fcx.infcx.take_opaque_types();
+
     // Consistency check our TypeckResults instance can hold all ItemLocalIds
     // it will need to hold.
     assert_eq!(typeck_results.hir_owner, id.owner);
diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs
index 4e9cb92919a..a580c114f26 100644
--- a/compiler/rustc_hir_typeck/src/method/confirm.rs
+++ b/compiler/rustc_hir_typeck/src/method/confirm.rs
@@ -518,12 +518,9 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
                         .report_mismatched_types(&cause, method_self_ty, self_ty, terr)
                         .emit();
                 } else {
-                    span_bug!(
-                        self.span,
-                        "{} was a subtype of {} but now is not?",
-                        self_ty,
-                        method_self_ty
-                    );
+                    error!("{self_ty} was a subtype of {method_self_ty} but now is not?");
+                    // This must already have errored elsewhere.
+                    self.dcx().has_errors().unwrap();
                 }
             }
         }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index ab5c0f32705..729ce1f00cd 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -1073,12 +1073,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                         // for instance
                                         self.tcx.at(span).type_of(*def_id).instantiate_identity()
                                             != rcvr_ty
-                                            && self
-                                                .tcx
-                                                .at(span)
-                                                .type_of(*def_id)
-                                                .instantiate_identity()
-                                                != rcvr_ty
                                     }
                                     (Mode::Path, false, _) => true,
                                     _ => false,
@@ -1092,7 +1086,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         inherent_impls_candidate.sort();
                         inherent_impls_candidate.dedup();
 
-                        // number of type to shows at most.
+                        // number of types to show at most
                         let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 };
                         let type_candidates = inherent_impls_candidate
                             .iter()
diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs
index 5ce80ef5c10..b83a0f893f5 100644
--- a/compiler/rustc_hir_typeck/src/writeback.rs
+++ b/compiler/rustc_hir_typeck/src/writeback.rs
@@ -221,8 +221,8 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
             if base_ty.is_none() {
                 // When encountering `return [0][0]` outside of a `fn` body we can encounter a base
                 // that isn't in the type table. We assume more relevant errors have already been
-                // emitted, so we delay an ICE if none have. (#64638)
-                self.tcx().dcx().span_delayed_bug(e.span, format!("bad base: `{base:?}`"));
+                // emitted. (#64638)
+                assert!(self.tcx().dcx().has_errors().is_some(), "bad base: `{base:?}`");
             }
             if let Some(base_ty) = base_ty
                 && let ty::Ref(_, base_ty_inner, _) = *base_ty.kind()
@@ -562,7 +562,15 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
 
     #[instrument(skip(self), level = "debug")]
     fn visit_opaque_types(&mut self) {
-        let opaque_types = self.fcx.infcx.take_opaque_types();
+        // We clone the opaques instead of stealing them here as they are still used for
+        // normalization in the next generation trait solver.
+        //
+        // FIXME(-Znext-solver): Opaque types defined after this would simply get dropped
+        // at the end of typeck. While this seems unlikely to happen in practice this
+        // should still get fixed. Either by preventing writeback from defining new opaque
+        // types or by using this function at the end of writeback and running it as a
+        // fixpoint.
+        let opaque_types = self.fcx.infcx.clone_opaque_types();
         for (opaque_type_key, decl) in opaque_types {
             let hidden_type = self.resolve(decl.hidden_type, &decl.hidden_type.span);
             let opaque_type_key = self.resolve(opaque_type_key, &decl.hidden_type.span);
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs
index 859593e1194..05b9479c7b0 100644
--- a/compiler/rustc_infer/src/infer/at.rs
+++ b/compiler/rustc_infer/src/infer/at.rs
@@ -285,13 +285,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
         T: Relate<'tcx>,
     {
         let Trace { at, trace, a_is_expected } = self;
-        at.infcx.commit_if_ok(|_| {
-            let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
-            fields
-                .sub(a_is_expected)
-                .relate(a, b)
-                .map(move |_| InferOk { value: (), obligations: fields.obligations })
-        })
+        let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
+        fields
+            .sub(a_is_expected)
+            .relate(a, b)
+            .map(move |_| InferOk { value: (), obligations: fields.obligations })
     }
 
     /// Makes `a == b`; the expectation is set by the call to
@@ -302,13 +300,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
         T: Relate<'tcx>,
     {
         let Trace { at, trace, a_is_expected } = self;
-        at.infcx.commit_if_ok(|_| {
-            let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
-            fields
-                .equate(a_is_expected)
-                .relate(a, b)
-                .map(move |_| InferOk { value: (), obligations: fields.obligations })
-        })
+        let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
+        fields
+            .equate(a_is_expected)
+            .relate(a, b)
+            .map(move |_| InferOk { value: (), obligations: fields.obligations })
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -317,13 +313,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
         T: Relate<'tcx>,
     {
         let Trace { at, trace, a_is_expected } = self;
-        at.infcx.commit_if_ok(|_| {
-            let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
-            fields
-                .lub(a_is_expected)
-                .relate(a, b)
-                .map(move |t| InferOk { value: t, obligations: fields.obligations })
-        })
+        let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
+        fields
+            .lub(a_is_expected)
+            .relate(a, b)
+            .map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 
     #[instrument(skip(self), level = "debug")]
@@ -332,13 +326,11 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
         T: Relate<'tcx>,
     {
         let Trace { at, trace, a_is_expected } = self;
-        at.infcx.commit_if_ok(|_| {
-            let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
-            fields
-                .glb(a_is_expected)
-                .relate(a, b)
-                .map(move |t| InferOk { value: t, obligations: fields.obligations })
-        })
+        let mut fields = at.infcx.combine_fields(trace, at.param_env, define_opaque_types);
+        fields
+            .glb(a_is_expected)
+            .relate(a, b)
+            .map(move |t| InferOk { value: t, obligations: fields.obligations })
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 216b2e70abf..56a45586c9d 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -620,42 +620,40 @@ impl<'tcx> InferCtxt<'tcx> {
         variables1: &OriginalQueryValues<'tcx>,
         variables2: impl Fn(BoundVar) -> GenericArg<'tcx>,
     ) -> InferResult<'tcx, ()> {
-        self.commit_if_ok(|_| {
-            let mut obligations = vec![];
-            for (index, value1) in variables1.var_values.iter().enumerate() {
-                let value2 = variables2(BoundVar::new(index));
-
-                match (value1.unpack(), value2.unpack()) {
-                    (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
-                        obligations.extend(
-                            self.at(cause, param_env)
-                                .eq(DefineOpaqueTypes::Yes, v1, v2)?
-                                .into_obligations(),
-                        );
-                    }
-                    (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
-                        if re1.is_erased() && re2.is_erased() =>
-                    {
-                        // no action needed
-                    }
-                    (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
-                        obligations.extend(
-                            self.at(cause, param_env)
-                                .eq(DefineOpaqueTypes::Yes, v1, v2)?
-                                .into_obligations(),
-                        );
-                    }
-                    (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
-                        let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?;
-                        obligations.extend(ok.into_obligations());
-                    }
-                    _ => {
-                        bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
-                    }
+        let mut obligations = vec![];
+        for (index, value1) in variables1.var_values.iter().enumerate() {
+            let value2 = variables2(BoundVar::new(index));
+
+            match (value1.unpack(), value2.unpack()) {
+                (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => {
+                    obligations.extend(
+                        self.at(cause, param_env)
+                            .eq(DefineOpaqueTypes::Yes, v1, v2)?
+                            .into_obligations(),
+                    );
+                }
+                (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2))
+                    if re1.is_erased() && re2.is_erased() =>
+                {
+                    // no action needed
+                }
+                (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => {
+                    obligations.extend(
+                        self.at(cause, param_env)
+                            .eq(DefineOpaqueTypes::Yes, v1, v2)?
+                            .into_obligations(),
+                    );
+                }
+                (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => {
+                    let ok = self.at(cause, param_env).eq(DefineOpaqueTypes::Yes, v1, v2)?;
+                    obligations.extend(ok.into_obligations());
+                }
+                _ => {
+                    bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,);
                 }
             }
-            Ok(InferOk { value: (), obligations })
-        })
+        }
+        Ok(InferOk { value: (), obligations })
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 4d2d19b51e2..b953b25d6c4 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -132,20 +132,6 @@ pub struct TypeErrCtxt<'a, 'tcx> {
         Box<dyn Fn(Ty<'tcx>) -> Vec<(Ty<'tcx>, Vec<PredicateObligation<'tcx>>)> + 'a>,
 }
 
-impl Drop for TypeErrCtxt<'_, '_> {
-    fn drop(&mut self) {
-        if self.dcx().has_errors().is_some() {
-            // Ok, emitted an error.
-        } else {
-            // Didn't emit an error; maybe it was created but not yet emitted.
-            self.infcx
-                .tcx
-                .sess
-                .good_path_delayed_bug("used a `TypeErrCtxt` without raising an error or lint");
-        }
-    }
-}
-
 impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
     pub fn dcx(&self) -> &'tcx DiagCtxt {
         self.infcx.tcx.dcx()
diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
index 6137506d4a9..c39d0425f7e 100644
--- a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
+++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs
@@ -802,14 +802,12 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
         }
 
         // Errors in earlier passes can yield error variables without
-        // resolution errors here; delay ICE in favor of those errors.
-        self.tcx().dcx().span_delayed_bug(
-            self.var_infos[node_idx].origin.span(),
-            format!(
-                "collect_error_for_expanding_node() could not find \
-                 error for var {node_idx:?} in universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \
-                 upper_bounds={upper_bounds:#?}"
-            ),
+        // resolution errors here; ICE if no errors have been emitted yet.
+        assert!(
+            self.tcx().dcx().has_errors().is_some(),
+            "collect_error_for_expanding_node() could not find error for var {node_idx:?} in \
+            universe {node_universe:?}, lower_bounds={lower_bounds:#?}, \
+            upper_bounds={upper_bounds:#?}",
         );
     }
 
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 0bf4598608f..2caf3b3cc93 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -1325,6 +1325,12 @@ impl<'tcx> InferCtxt<'tcx> {
         std::mem::take(&mut self.inner.borrow_mut().opaque_type_storage.opaque_types)
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
+    pub fn clone_opaque_types(&self) -> opaque_types::OpaqueTypeMap<'tcx> {
+        debug_assert_ne!(self.defining_use_anchor, DefiningAnchor::Error);
+        self.inner.borrow().opaque_type_storage.opaque_types.clone()
+    }
+
     pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
         self.resolve_vars_if_possible(t).to_string()
     }
diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs
index 6a684dba8de..9f49ed00219 100644
--- a/compiler/rustc_infer/src/infer/opaque_types/table.rs
+++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs
@@ -20,7 +20,8 @@ impl<'tcx> OpaqueTypeStorage<'tcx> {
         if let Some(idx) = idx {
             self.opaque_types.get_mut(&key).unwrap().hidden_type = idx;
         } else {
-            match self.opaque_types.remove(&key) {
+            // FIXME(#120456) - is `swap_remove` correct?
+            match self.opaque_types.swap_remove(&key) {
                 None => bug!("reverted opaque type inference that was never registered: {:?}", key),
                 Some(_) => {}
             }
diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs
index 7208f17fb34..c0a99e5cc41 100644
--- a/compiler/rustc_infer/src/infer/outlives/obligations.rs
+++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs
@@ -300,9 +300,9 @@ where
                     self.components_must_outlive(origin, subcomponents, region, category);
                 }
                 Component::UnresolvedInferenceVariable(v) => {
-                    // ignore this, we presume it will yield an error
-                    // later, since if a type variable is not resolved by
-                    // this point it never will be
+                    // Ignore this, we presume it will yield an error later,
+                    // since if a type variable is not resolved by this point
+                    // it never will be.
                     self.tcx.dcx().span_delayed_bug(
                         origin.span(),
                         format!("unresolved inference variable in outlives: {v:?}"),
diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs
index 24b351988bf..3ef37bf3466 100644
--- a/compiler/rustc_infer/src/infer/outlives/verify.rs
+++ b/compiler/rustc_infer/src/infer/outlives/verify.rs
@@ -172,13 +172,13 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
                 self.bound_from_components(components, visited)
             }
             Component::UnresolvedInferenceVariable(v) => {
-                // ignore this, we presume it will yield an error
-                // later, since if a type variable is not resolved by
-                // this point it never will be
+                // Ignore this, we presume it will yield an error later, since
+                // if a type variable is not resolved by this point it never
+                // will be.
                 self.tcx
                     .dcx()
                     .delayed_bug(format!("unresolved inference variable in outlives: {v:?}"));
-                // add a bound that never holds
+                // Add a bound that never holds.
                 VerifyBound::AnyBound(vec![])
             }
         }
diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs
index 5ae080d4702..0862204d88e 100644
--- a/compiler/rustc_lint/src/early.rs
+++ b/compiler/rustc_lint/src/early.rs
@@ -431,14 +431,13 @@ pub fn check_ast_node_inner<'a, T: EarlyLintPass>(
     // If not, that means that we somehow buffered a lint for a node id
     // that was not lint-checked (perhaps it doesn't exist?). This is a bug.
     for (id, lints) in cx.context.buffered.map {
-        for early_lint in lints {
-            sess.dcx().span_delayed_bug(
-                early_lint.span,
-                format!(
-                    "failed to process buffered lint here (dummy = {})",
-                    id == ast::DUMMY_NODE_ID
-                ),
+        if !lints.is_empty() {
+            assert!(
+                sess.dcx().has_errors().is_some(),
+                "failed to process buffered lint here (dummy = {})",
+                id == ast::DUMMY_NODE_ID
             );
+            break;
         }
     }
 }
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 9f670893b27..35ee0c53046 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -651,9 +651,11 @@ trait UnusedDelimLint {
 
     fn is_expr_delims_necessary(
         inner: &ast::Expr,
+        ctx: UnusedDelimsCtx,
         followed_by_block: bool,
-        followed_by_else: bool,
     ) -> bool {
+        let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
+
         if followed_by_else {
             match inner.kind {
                 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
@@ -662,6 +664,13 @@ trait UnusedDelimLint {
             }
         }
 
+        // Check it's range in LetScrutineeExpr
+        if let ast::ExprKind::Range(..) = inner.kind
+            && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
+        {
+            return true;
+        }
+
         // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`.
         {
             let mut innermost = inner;
@@ -1007,8 +1016,7 @@ impl UnusedDelimLint for UnusedParens {
     ) {
         match value.kind {
             ast::ExprKind::Paren(ref inner) => {
-                let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
-                if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else)
+                if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
                     && value.attrs.is_empty()
                     && !value.span.from_expansion()
                     && (ctx != UnusedDelimsCtx::LetScrutineeExpr
@@ -1334,7 +1342,7 @@ impl UnusedDelimLint for UnusedBraces {
                 // FIXME(const_generics): handle paths when #67075 is fixed.
                 if let [stmt] = inner.stmts.as_slice() {
                     if let ast::StmtKind::Expr(ref expr) = stmt.kind {
-                        if !Self::is_expr_delims_necessary(expr, followed_by_block, false)
+                        if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
                             && (ctx != UnusedDelimsCtx::AnonConst
                                 || (matches!(expr.kind, ast::ExprKind::Lit(_))
                                     && !expr.span.from_expansion()))
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 7ed78a2ffc8..09b1f03f151 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -708,7 +708,8 @@ impl LintBuffer {
     }
 
     pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
-        self.map.remove(&id).unwrap_or_default()
+        // FIXME(#120456) - is `swap_remove` correct?
+        self.map.swap_remove(&id).unwrap_or_default()
     }
 
     pub fn buffer_lint(
diff --git a/compiler/rustc_llvm/Cargo.toml b/compiler/rustc_llvm/Cargo.toml
index 58e219e5a46..6598f1db86d 100644
--- a/compiler/rustc_llvm/Cargo.toml
+++ b/compiler/rustc_llvm/Cargo.toml
@@ -8,7 +8,12 @@ edition = "2021"
 libc = "0.2.73"
 # tidy-alphabetical-end
 
+# FIXME: updating cc past 1.0.79 breaks libstd bootstrapping, pin
+# to the last working version here so `cargo update` doesn't cause the
+# a higher version to be selected
+# https://github.com/rust-lang/cc-rs/issues/913
+# 1.0.{84, 85} fix this but have been yanked
 [build-dependencies]
 # tidy-alphabetical-start
-cc = "1.0.69"
+cc = "=1.0.79"
 # tidy-alphabetical-end
diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
index 373bc5cc581..627be997513 100644
--- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp
@@ -139,7 +139,7 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer(
            RustMappingRegions, NumMappingRegions)) {
     MappingRegions.emplace_back(
         fromRust(Region.Count), fromRust(Region.FalseCount),
-#if LLVM_VERSION_GE(18, 0)
+#if LLVM_VERSION_GE(18, 0) && LLVM_VERSION_LT(19, 0)
         coverage::CounterMappingRegion::MCDCParameters{},
 #endif
         Region.FileID, Region.ExpandedFileID,
diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
index 3b6bf03686b..5b1cc52f1f6 100644
--- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp
@@ -43,6 +43,9 @@
 #include "llvm/Transforms/Instrumentation.h"
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
 #include "llvm/Support/TimeProfiler.h"
+#if LLVM_VERSION_GE(19, 0)
+#include "llvm/Support/PGOOptions.h"
+#endif
 #include "llvm/Transforms/Instrumentation/GCOVProfiler.h"
 #include "llvm/Transforms/Instrumentation/InstrProfiling.h"
 #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
@@ -368,10 +371,10 @@ extern "C" void LLVMRustPrintTargetCPUs(LLVMTargetMachineRef TM,
 }
 
 extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
-#ifdef LLVM_RUSTLLVM
+#if LLVM_VERSION_GE(18, 0)
   const TargetMachine *Target = unwrap(TM);
   const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
-  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
+  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
   return FeatTable.size();
 #else
   return 0;
@@ -380,10 +383,10 @@ extern "C" size_t LLVMRustGetTargetFeaturesCount(LLVMTargetMachineRef TM) {
 
 extern "C" void LLVMRustGetTargetFeature(LLVMTargetMachineRef TM, size_t Index,
                                          const char** Feature, const char** Desc) {
-#ifdef LLVM_RUSTLLVM
+#if LLVM_VERSION_GE(18, 0)
   const TargetMachine *Target = unwrap(TM);
   const MCSubtargetInfo *MCInfo = Target->getMCSubtargetInfo();
-  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getFeatureTable();
+  const ArrayRef<SubtargetFeatureKV> FeatTable = MCInfo->getAllProcessorFeatures();
   const SubtargetFeatureKV Feat = FeatTable[Index];
   *Feature = Feat.Key;
   *Desc = Feat.Desc;
@@ -749,6 +752,9 @@ LLVMRustOptimize(
                         FS,
 #endif
                         PGOOptions::IRInstr, PGOOptions::NoCSAction,
+#if LLVM_VERSION_GE(19, 0)
+                        PGOOptions::ColdFuncOpt::Default,
+#endif
                         DebugInfoForProfiling);
   } else if (PGOUsePath) {
     assert(!PGOSampleUsePath);
@@ -758,6 +764,9 @@ LLVMRustOptimize(
                         FS,
 #endif
                         PGOOptions::IRUse, PGOOptions::NoCSAction,
+#if LLVM_VERSION_GE(19, 0)
+                        PGOOptions::ColdFuncOpt::Default,
+#endif
                         DebugInfoForProfiling);
   } else if (PGOSampleUsePath) {
     PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
@@ -766,6 +775,9 @@ LLVMRustOptimize(
                         FS,
 #endif
                         PGOOptions::SampleUse, PGOOptions::NoCSAction,
+#if LLVM_VERSION_GE(19, 0)
+                        PGOOptions::ColdFuncOpt::Default,
+#endif
                         DebugInfoForProfiling);
   } else if (DebugInfoForProfiling) {
     PGOOpt = PGOOptions("", "", "",
@@ -774,6 +786,9 @@ LLVMRustOptimize(
                         FS,
 #endif
                         PGOOptions::NoAction, PGOOptions::NoCSAction,
+#if LLVM_VERSION_GE(19, 0)
+                        PGOOptions::ColdFuncOpt::Default,
+#endif
                         DebugInfoForProfiling);
   }
 
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index a2dfebec594..b45706fd1e5 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -1801,6 +1801,9 @@ extern "C" LLVMRustResult LLVMRustWriteImportLibrary(
       std::string{},    // ExtName
       std::string{},    // SymbolName
       std::string{},    // AliasTarget
+#if LLVM_VERSION_GE(19, 0)
+      std::string{},    // ExportAs
+#endif
       ordinal,          // Ordinal
       ordinal_present,  // Noname
       false,            // Data
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 8e1cb6a514f..e7d9dc04886 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -617,7 +617,7 @@ impl<'hir> Map<'hir> {
                 Node::Item(_)
                 | Node::ForeignItem(_)
                 | Node::TraitItem(_)
-                | Node::Expr(Expr { kind: ExprKind::Closure { .. }, .. })
+                | Node::Expr(Expr { kind: ExprKind::Closure(_), .. })
                 | Node::ImplItem(_)
                     // The input node `id` must be enclosed in the method's body as opposed
                     // to some other place such as its return type (fixes #114918).
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 6ffa0819f35..2a6f473cd32 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -207,6 +207,11 @@ pub fn explain_lint_level_source(
     err: &mut Diagnostic,
 ) {
     let name = lint.name_lower();
+    if let Level::Allow = level {
+        // Do not point at `#[allow(compat_lint)]` as the reason for a compatibility lint
+        // triggering. (#121009)
+        return;
+    }
     match src {
         LintLevelSource::Default => {
             err.note_once(format!("`#[{}({})]` on by default", level.as_str(), name));
diff --git a/compiler/rustc_middle/src/ty/consts.rs b/compiler/rustc_middle/src/ty/consts.rs
index 23771073745..5e4d899f517 100644
--- a/compiler/rustc_middle/src/ty/consts.rs
+++ b/compiler/rustc_middle/src/ty/consts.rs
@@ -35,6 +35,16 @@ impl<'tcx> IntoKind for Const<'tcx> {
     }
 }
 
+impl<'tcx> rustc_type_ir::visit::Flags for Const<'tcx> {
+    fn flags(&self) -> TypeFlags {
+        self.0.flags
+    }
+
+    fn outer_exclusive_binder(&self) -> rustc_type_ir::DebruijnIndex {
+        self.0.outer_exclusive_binder
+    }
+}
+
 impl<'tcx> ConstTy<TyCtxt<'tcx>> for Const<'tcx> {
     fn ty(self) -> Ty<'tcx> {
         self.ty()
@@ -63,11 +73,13 @@ impl<'tcx> Const<'tcx> {
         self.0.kind
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline]
     pub fn flags(self) -> TypeFlags {
         self.0.flags
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline]
     pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
         self.0.outer_exclusive_binder
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index bd86c1c284e..7033a4a833d 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -28,7 +28,7 @@ use crate::ty::{
     self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
     ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate,
     PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
-    Visibility,
+    TypeVisitable, Visibility,
 };
 use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
 use rustc_ast::{self as ast, attr};
@@ -87,7 +87,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
     type GenericArg = ty::GenericArg<'tcx>;
     type Term = ty::Term<'tcx>;
 
-    type Binder<T> = Binder<'tcx, T>;
+    type Binder<T: TypeVisitable<TyCtxt<'tcx>>> = Binder<'tcx, T>;
+    type BoundVars = &'tcx List<ty::BoundVariableKind>;
+    type BoundVar = ty::BoundVariableKind;
     type CanonicalVars = CanonicalVarInfos<'tcx>;
 
     type Ty = Ty<'tcx>;
@@ -151,6 +153,11 @@ 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>, ()>;
@@ -1046,12 +1053,6 @@ impl<'tcx> TyCtxtAt<'tcx> {
         name: Symbol,
         def_kind: DefKind,
     ) -> TyCtxtFeed<'tcx, LocalDefId> {
-        // This function modifies `self.definitions` using a side-effect.
-        // We need to ensure that these side effects are re-run by the incr. comp. engine.
-        // Depending on the forever-red node will tell the graph that the calling query
-        // needs to be re-evaluated.
-        self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
-
         // The following call has the side effect of modifying the tables inside `definitions`.
         // These very tables are relied on by the incr. comp. engine to decode DepNodes and to
         // decode the on-disk cache.
@@ -1080,6 +1081,12 @@ impl<'tcx> TyCtxt<'tcx> {
         let data = def_kind.def_path_data(name);
         let def_id = self.untracked.definitions.write().create_def(parent, data);
 
+        // This function modifies `self.definitions` using a side-effect.
+        // We need to ensure that these side effects are re-run by the incr. comp. engine.
+        // Depending on the forever-red node will tell the graph that the calling query
+        // needs to be re-evaluated.
+        self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
+
         let feed = self.feed_local_def_id(def_id);
         feed.def_kind(def_kind);
         // Unique types created for closures participate in type privacy checking.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index 6ee74ef2fb6..15bddb2a64f 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -252,6 +252,7 @@ pub struct ImplHeader<'tcx> {
 pub struct ImplTraitHeader<'tcx> {
     pub trait_ref: ty::TraitRef<'tcx>,
     pub polarity: ImplPolarity,
+    pub unsafety: hir::Unsafety,
 }
 
 #[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
@@ -503,6 +504,16 @@ impl<'tcx> IntoKind for Ty<'tcx> {
     }
 }
 
+impl<'tcx> rustc_type_ir::visit::Flags for Ty<'tcx> {
+    fn flags(&self) -> TypeFlags {
+        self.0.flags
+    }
+
+    fn outer_exclusive_binder(&self) -> DebruijnIndex {
+        self.0.outer_exclusive_binder
+    }
+}
+
 impl EarlyParamRegion {
     /// Does this early bound region have a name? Early bound regions normally
     /// always have names except when using anonymous lifetimes (`'_`).
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index 200811940ed..b63f9c6dfa0 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -29,6 +29,16 @@ pub struct Predicate<'tcx>(
     pub(super) Interned<'tcx, WithCachedTypeInfo<ty::Binder<'tcx, PredicateKind<'tcx>>>>,
 );
 
+impl<'tcx> rustc_type_ir::visit::Flags for Predicate<'tcx> {
+    fn flags(&self) -> TypeFlags {
+        self.0.flags
+    }
+
+    fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
+        self.0.outer_exclusive_binder
+    }
+}
+
 impl<'tcx> Predicate<'tcx> {
     /// Gets the inner `ty::Binder<'tcx, PredicateKind<'tcx>>`.
     #[inline]
@@ -36,11 +46,13 @@ impl<'tcx> Predicate<'tcx> {
         self.0.internee
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline(always)]
     pub fn flags(self) -> TypeFlags {
         self.0.flags
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline(always)]
     pub fn outer_exclusive_binder(self) -> DebruijnIndex {
         self.0.outer_exclusive_binder
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index 5cf90e94907..92ec1a83bee 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -3156,13 +3156,12 @@ fn for_each_def(tcx: TyCtxt<'_>, mut collect_fn: impl for<'b> FnMut(&'b Ident, N
 // this is pub to be able to intra-doc-link it
 pub fn trimmed_def_paths(tcx: TyCtxt<'_>, (): ()) -> DefIdMap<Symbol> {
     // Trimming paths is expensive and not optimized, since we expect it to only be used for error
-    // reporting.
+    // reporting. Record the fact that we did it, so we can abort if we later found it was
+    // unnecessary.
     //
-    // For good paths causing this bug, the `rustc_middle::ty::print::with_no_trimmed_paths`
-    // wrapper can be used to suppress this query, in exchange for full paths being formatted.
-    tcx.sess.good_path_delayed_bug(
-        "trimmed_def_paths constructed but no error emitted; use `DelayDm` for lints or `with_no_trimmed_paths` for debugging",
-    );
+    // The `rustc_middle::ty::print::with_no_trimmed_paths` wrapper can be used to suppress this
+    // checking, in exchange for full paths being formatted.
+    tcx.sess.record_trimmed_def_paths();
 
     // Once constructed, unique namespace+symbol pairs will have a `Some(_)` entry, while
     // non-unique pairs will have a `None` entry.
diff --git a/compiler/rustc_middle/src/ty/region.rs b/compiler/rustc_middle/src/ty/region.rs
index 1191d7fca32..b206727f051 100644
--- a/compiler/rustc_middle/src/ty/region.rs
+++ b/compiler/rustc_middle/src/ty/region.rs
@@ -26,6 +26,19 @@ impl<'tcx> rustc_type_ir::IntoKind for Region<'tcx> {
     }
 }
 
+impl<'tcx> rustc_type_ir::visit::Flags for Region<'tcx> {
+    fn flags(&self) -> TypeFlags {
+        self.type_flags()
+    }
+
+    fn outer_exclusive_binder(&self) -> ty::DebruijnIndex {
+        match **self {
+            ty::ReBound(debruijn, _) => debruijn.shifted_in(1),
+            _ => ty::INNERMOST,
+        }
+    }
+}
+
 impl<'tcx> Region<'tcx> {
     #[inline]
     pub fn new_early_param(
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index a3d5f1f1955..66086ac87f1 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -942,6 +942,16 @@ where
     }
 }
 
+impl<'tcx, T> rustc_type_ir::BoundVars<TyCtxt<'tcx>> for ty::Binder<'tcx, T> {
+    fn bound_vars(&self) -> &'tcx List<ty::BoundVariableKind> {
+        self.bound_vars
+    }
+
+    fn has_no_bound_vars(&self) -> bool {
+        self.bound_vars.is_empty()
+    }
+}
+
 impl<'tcx, T> Binder<'tcx, T> {
     /// Skips the binder and returns the "bound" value. This is a
     /// risky thing to do because it's easy to get confused about
@@ -1808,6 +1818,7 @@ impl<'tcx> Ty<'tcx> {
         self.0.0
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline(always)]
     pub fn flags(self) -> TypeFlags {
         self.0.0.flags
@@ -2362,6 +2373,20 @@ impl<'tcx> Ty<'tcx> {
     /// to represent the closure kind, because it has not yet been
     /// inferred. Once upvar inference (in `rustc_hir_analysis/src/check/upvar.rs`)
     /// is complete, that type variable will be unified.
+    ///
+    /// To be noted that you can use [`ClosureArgs::kind()`] or [`CoroutineClosureArgs::kind()`]
+    /// to get the same information, which you can get by calling [`GenericArgs::as_closure()`]
+    /// or [`GenericArgs::as_coroutine_closure()`], depending on the type of the closure.
+    ///
+    /// Otherwise, this method can be used as follows:
+    ///
+    /// ```rust,ignore (snippet of compiler code)
+    /// let TyKind::Closure(def_id, [closure_fn_kind_ty, ..]) = closure_ty.kind()
+    ///     && let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind()
+    /// {
+    ///     // your code
+    /// }
+    /// ```
     pub fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> {
         match self.kind() {
             Int(int_ty) => match int_ty {
diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs
index c674a868d9f..09bb06de483 100644
--- a/compiler/rustc_middle/src/ty/util.rs
+++ b/compiler/rustc_middle/src/ty/util.rs
@@ -1320,6 +1320,7 @@ impl<'tcx> Ty<'tcx> {
         ty
     }
 
+    // FIXME(compiler-errors): Think about removing this.
     #[inline]
     pub fn outer_exclusive_binder(self) -> ty::DebruijnIndex {
         self.0.outer_exclusive_binder
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index 7acdb931f1a..59292a281ed 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -1,140 +1,10 @@
 use crate::ty::{self, Binder, Ty, TyCtxt, TypeFlags};
-use rustc_errors::ErrorGuaranteed;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sso::SsoHashSet;
 use std::ops::ControlFlow;
 
-pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-
-pub trait TypeVisitableExt<'tcx>: TypeVisitable<TyCtxt<'tcx>> {
-    /// Returns `true` if `self` has any late-bound regions that are either
-    /// bound by `binder` or bound by some binder outside of `binder`.
-    /// If `binder` is `ty::INNERMOST`, this indicates whether
-    /// there are any late-bound regions that appear free.
-    fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
-        self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
-    }
-
-    /// Returns `true` if this type has any regions that escape `binder` (and
-    /// hence are not bound by it).
-    fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
-        self.has_vars_bound_at_or_above(binder.shifted_in(1))
-    }
-
-    /// Return `true` if this type has regions that are not a part of the type.
-    /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
-    /// would return `true`. The latter can occur when traversing through the
-    /// former.
-    ///
-    /// See [`HasEscapingVarsVisitor`] for more information.
-    fn has_escaping_bound_vars(&self) -> bool {
-        self.has_vars_bound_at_or_above(ty::INNERMOST)
-    }
-
-    fn has_type_flags(&self, flags: TypeFlags) -> bool {
-        let res =
-            self.visit_with(&mut HasTypeFlagsVisitor { flags }).break_value() == Some(FoundFlags);
-        trace!(?self, ?flags, ?res, "has_type_flags");
-        res
-    }
-    fn has_projections(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PROJECTION)
-    }
-    fn has_inherent_projections(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
-    }
-    fn has_opaque_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
-    }
-    fn has_coroutines(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
-    }
-    fn references_error(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_ERROR)
-    }
-    fn error_reported(&self) -> Result<(), ErrorGuaranteed> {
-        if self.references_error() {
-            // We must include lint errors and delayed bugs here.
-            if let Some(reported) =
-                ty::tls::with(|tcx| tcx.dcx().has_errors_or_lint_errors_or_delayed_bugs())
-            {
-                Err(reported)
-            } else {
-                bug!("expected some kind of error in `error_reported`");
-            }
-        } else {
-            Ok(())
-        }
-    }
-    fn has_non_region_param(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
-    }
-    fn has_infer_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_RE_INFER)
-    }
-    fn has_infer_types(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_TY_INFER)
-    }
-    fn has_non_region_infer(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
-    }
-    fn has_infer(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_INFER)
-    }
-    fn has_placeholders(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
-    }
-    fn has_non_region_placeholders(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
-    }
-    fn has_param(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_PARAM)
-    }
-    /// "Free" regions in this context means that it has any region
-    /// that is not (a) erased or (b) late-bound.
-    fn has_free_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
-    }
-
-    fn has_erased_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_RE_ERASED)
-    }
-
-    /// True if there are any un-erased free regions.
-    fn has_erasable_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
-    }
-
-    /// Indicates whether this value references only 'global'
-    /// generic parameters that are the same regardless of what fn we are
-    /// in. This is used for caching.
-    fn is_global(&self) -> bool {
-        !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
-    }
-
-    /// True if there are any late-bound regions
-    fn has_bound_regions(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_RE_BOUND)
-    }
-    /// True if there are any late-bound non-region variables
-    fn has_non_region_bound_vars(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND)
-    }
-    /// True if there are any bound variables
-    fn has_bound_vars(&self) -> bool {
-        self.has_type_flags(TypeFlags::HAS_BOUND_VARS)
-    }
-
-    /// Indicates whether this value still has parameters/placeholders/inference variables
-    /// which could be replaced later, in a way that would change the results of `impl`
-    /// specialization.
-    fn still_further_specializable(&self) -> bool {
-        self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
-    }
-}
-
-impl<'tcx, T: TypeVisitable<TyCtxt<'tcx>>> TypeVisitableExt<'tcx> for T {}
+pub use rustc_type_ir::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor};
 
 ///////////////////////////////////////////////////////////////////////////
 // Region folder
@@ -370,185 +240,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ValidateBoundVars<'tcx> {
     }
 }
 
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-struct FoundEscapingVars;
-
-/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
-/// bound region or a bound type.
-///
-/// So, for example, consider a type like the following, which has two binders:
-///
-///    for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
-///    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
-///                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  inner scope
-///
-/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
-/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
-/// fn type*, that type has an escaping region: `'a`.
-///
-/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
-/// we already use the term "free var". It refers to the regions or types that we use to represent
-/// bound regions or type params on a fn definition while we are type checking its body.
-///
-/// To clarify, conceptually there is no particular difference between
-/// an "escaping" var and a "free" var. However, there is a big
-/// difference in practice. Basically, when "entering" a binding
-/// level, one is generally required to do some sort of processing to
-/// a bound var, such as replacing it with a fresh/placeholder
-/// var, or making an entry in the environment to represent the
-/// scope to which it is attached, etc. An escaping var represents
-/// a bound var for which this processing has not yet been done.
-struct HasEscapingVarsVisitor {
-    /// Anything bound by `outer_index` or "above" is escaping.
-    outer_index: ty::DebruijnIndex,
-}
-
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasEscapingVarsVisitor {
-    type BreakTy = FoundEscapingVars;
-
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
-        self.outer_index.shift_in(1);
-        let result = t.super_visit_with(self);
-        self.outer_index.shift_out(1);
-        result
-    }
-
-    #[inline]
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // If the outer-exclusive-binder is *strictly greater* than
-        // `outer_index`, that means that `t` contains some content
-        // bound at `outer_index` or above (because
-        // `outer_exclusive_binder` is always 1 higher than the
-        // content in `t`). Therefore, `t` has some escaping vars.
-        if t.outer_exclusive_binder() > self.outer_index {
-            ControlFlow::Break(FoundEscapingVars)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    #[inline]
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // If the region is bound by `outer_index` or anything outside
-        // of outer index, then it escapes the binders we have
-        // visited.
-        if r.bound_at_or_above_binder(self.outer_index) {
-            ControlFlow::Break(FoundEscapingVars)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    fn visit_const(&mut self, ct: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // If the outer-exclusive-binder is *strictly greater* than
-        // `outer_index`, that means that `ct` contains some content
-        // bound at `outer_index` or above (because
-        // `outer_exclusive_binder` is always 1 higher than the
-        // content in `t`). Therefore, `t` has some escaping vars.
-        if ct.outer_exclusive_binder() > self.outer_index {
-            ControlFlow::Break(FoundEscapingVars)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    #[inline]
-    fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
-        if predicate.outer_exclusive_binder() > self.outer_index {
-            ControlFlow::Break(FoundEscapingVars)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-struct FoundFlags;
-
-// FIXME: Optimize for checking for infer flags
-struct HasTypeFlagsVisitor {
-    flags: ty::TypeFlags,
-}
-
-impl std::fmt::Debug for HasTypeFlagsVisitor {
-    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        self.flags.fmt(fmt)
-    }
-}
-
-// Note: this visitor traverses values down to the level of
-// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
-// type flags at the outer layer are enough. So it's faster than it first
-// looks, particular for `Ty`/`Predicate` where it's just a field access.
-//
-// N.B. The only case where this isn't totally true is binders, which also
-// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
-// are present, regardless of whether those bound variables are used. This
-// is important for anonymization of binders in `TyCtxt::erase_regions`. We
-// specifically detect this case in `visit_binder`.
-impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for HasTypeFlagsVisitor {
-    type BreakTy = FoundFlags;
-
-    fn visit_binder<T: TypeVisitable<TyCtxt<'tcx>>>(
-        &mut self,
-        t: &Binder<'tcx, T>,
-    ) -> ControlFlow<Self::BreakTy> {
-        // If we're looking for the HAS_BINDER_VARS flag, check if the
-        // binder has vars. This won't be present in the binder's bound
-        // value, so we need to check here too.
-        if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.bound_vars().is_empty() {
-            return ControlFlow::Break(FoundFlags);
-        }
-
-        t.super_visit_with(self)
-    }
-
-    #[inline]
-    fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // Note: no `super_visit_with` call.
-        let flags = t.flags();
-        if flags.intersects(self.flags) {
-            ControlFlow::Break(FoundFlags)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    #[inline]
-    fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // Note: no `super_visit_with` call, as usual for `Region`.
-        let flags = r.type_flags();
-        if flags.intersects(self.flags) {
-            ControlFlow::Break(FoundFlags)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    #[inline]
-    fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // Note: no `super_visit_with` call.
-        if c.flags().intersects(self.flags) {
-            ControlFlow::Break(FoundFlags)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-
-    #[inline]
-    fn visit_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ControlFlow<Self::BreakTy> {
-        // Note: no `super_visit_with` call.
-        if predicate.flags().intersects(self.flags) {
-            ControlFlow::Break(FoundFlags)
-        } else {
-            ControlFlow::Continue(())
-        }
-    }
-}
-
 /// Collects all the late-bound regions at the innermost binding level
 /// into a hash set.
 struct LateBoundRegionsCollector {
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 906b3205ca7..35f5a6bfac5 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -319,7 +319,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // them.
         let mut fake_borrows = match_has_guard.then(FxIndexSet::default);
 
-        let mut otherwise = None;
+        let otherwise_block = self.cfg.start_new_block();
 
         // This will generate code to test scrutinee_place and
         // branch to the appropriate arm block
@@ -327,46 +327,44 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             match_start_span,
             scrutinee_span,
             block,
-            &mut otherwise,
+            otherwise_block,
             candidates,
             &mut fake_borrows,
         );
 
-        if let Some(otherwise_block) = otherwise {
-            // See the doc comment on `match_candidates` for why we may have an
-            // otherwise block. Match checking will ensure this is actually
-            // unreachable.
-            let source_info = self.source_info(scrutinee_span);
-
-            // Matching on a `scrutinee_place` with an uninhabited type doesn't
-            // generate any memory reads by itself, and so if the place "expression"
-            // contains unsafe operations like raw pointer dereferences or union
-            // field projections, we wouldn't know to require an `unsafe` block
-            // around a `match` equivalent to `std::intrinsics::unreachable()`.
-            // See issue #47412 for this hole being discovered in the wild.
-            //
-            // HACK(eddyb) Work around the above issue by adding a dummy inspection
-            // of `scrutinee_place`, specifically by applying `ReadForMatch`.
-            //
-            // NOTE: ReadForMatch also checks that the scrutinee is initialized.
-            // This is currently needed to not allow matching on an uninitialized,
-            // uninhabited value. If we get never patterns, those will check that
-            // the place is initialized, and so this read would only be used to
-            // check safety.
-            let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
-
-            if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
-                self.cfg.push_fake_read(
-                    otherwise_block,
-                    source_info,
-                    cause_matched_place,
-                    scrutinee_place,
-                );
-            }
+        // See the doc comment on `match_candidates` for why we may have an
+        // otherwise block. Match checking will ensure this is actually
+        // unreachable.
+        let source_info = self.source_info(scrutinee_span);
+
+        // Matching on a `scrutinee_place` with an uninhabited type doesn't
+        // generate any memory reads by itself, and so if the place "expression"
+        // contains unsafe operations like raw pointer dereferences or union
+        // field projections, we wouldn't know to require an `unsafe` block
+        // around a `match` equivalent to `std::intrinsics::unreachable()`.
+        // See issue #47412 for this hole being discovered in the wild.
+        //
+        // HACK(eddyb) Work around the above issue by adding a dummy inspection
+        // of `scrutinee_place`, specifically by applying `ReadForMatch`.
+        //
+        // NOTE: ReadForMatch also checks that the scrutinee is initialized.
+        // This is currently needed to not allow matching on an uninitialized,
+        // uninhabited value. If we get never patterns, those will check that
+        // the place is initialized, and so this read would only be used to
+        // check safety.
+        let cause_matched_place = FakeReadCause::ForMatchedPlace(None);
 
-            self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
+        if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
+            self.cfg.push_fake_read(
+                otherwise_block,
+                source_info,
+                cause_matched_place,
+                scrutinee_place,
+            );
         }
 
+        self.cfg.terminate(otherwise_block, source_info, TerminatorKind::Unreachable);
+
         // Link each leaf candidate to the `pre_binding_block` of the next one.
         let mut previous_candidate: Option<&mut Candidate<'_, '_>> = None;
 
@@ -1163,7 +1161,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         span: Span,
         scrutinee_span: Span,
         start_block: BasicBlock,
-        otherwise_block: &mut Option<BasicBlock>,
+        otherwise_block: BasicBlock,
         candidates: &mut [&mut Candidate<'pat, 'tcx>],
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
@@ -1210,7 +1208,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         span: Span,
         scrutinee_span: Span,
         start_block: BasicBlock,
-        otherwise_block: &mut Option<BasicBlock>,
+        otherwise_block: BasicBlock,
         candidates: &mut [&mut Candidate<'_, 'tcx>],
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
@@ -1243,11 +1241,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // never reach this point.
         if unmatched_candidates.is_empty() {
             let source_info = self.source_info(span);
-            if let Some(otherwise) = *otherwise_block {
-                self.cfg.goto(block, source_info, otherwise);
-            } else {
-                *otherwise_block = Some(block);
-            }
+            self.cfg.goto(block, source_info, otherwise_block);
             return;
         }
 
@@ -1428,7 +1422,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         scrutinee_span: Span,
         candidates: &mut [&mut Candidate<'_, 'tcx>],
         block: BasicBlock,
-        otherwise_block: &mut Option<BasicBlock>,
+        otherwise_block: BasicBlock,
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
         let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
@@ -1453,7 +1447,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         let match_pairs = mem::take(&mut first_candidate.match_pairs);
         first_candidate.pre_binding_block = Some(block);
 
-        let mut otherwise = None;
+        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");
@@ -1463,7 +1457,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             first_candidate.visit_leaves(|leaf_candidate| {
                 self.test_or_pattern(
                     leaf_candidate,
-                    &mut otherwise,
+                    remainder_start,
                     pats,
                     or_span,
                     &match_pair.place,
@@ -1472,8 +1466,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             });
         }
 
-        let remainder_start = otherwise.unwrap_or_else(|| self.cfg.start_new_block());
-
         self.match_candidates(
             span,
             scrutinee_span,
@@ -1491,7 +1483,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     fn test_or_pattern<'pat>(
         &mut self,
         candidate: &mut Candidate<'pat, 'tcx>,
-        otherwise: &mut Option<BasicBlock>,
+        otherwise: BasicBlock,
         pats: &'pat [Box<Pat<'tcx>>],
         or_span: Span,
         place: &PlaceBuilder<'tcx>,
@@ -1503,8 +1495,8 @@ 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 candidate.otherwise_block.is_some() {
-            &mut candidate.otherwise_block
+        let otherwise = if let Some(otherwise_block) = candidate.otherwise_block {
+            otherwise_block
         } else {
             otherwise
         };
@@ -1680,8 +1672,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         span: Span,
         scrutinee_span: Span,
         mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
-        block: BasicBlock,
-        otherwise_block: &mut Option<BasicBlock>,
+        start_block: BasicBlock,
+        otherwise_block: BasicBlock,
         fake_borrows: &mut Option<FxIndexSet<Place<'tcx>>>,
     ) {
         // extract the match-pair from the highest priority candidate
@@ -1749,12 +1741,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         debug!("untested_candidates: {}", candidates.len());
 
         // The block that we should branch to if none of the
-        // `target_candidates` match. This is either the block where we
-        // start matching the untested candidates if there are any,
-        // otherwise it's the `otherwise_block`.
-        let remainder_start = &mut None;
-        let remainder_start =
-            if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
+        // `target_candidates` match.
+        let remainder_start = if !candidates.is_empty() {
+            let remainder_start = self.cfg.start_new_block();
+            self.match_candidates(
+                span,
+                scrutinee_span,
+                remainder_start,
+                otherwise_block,
+                candidates,
+                fake_borrows,
+            );
+            remainder_start
+        } else {
+            otherwise_block
+        };
 
         // For each outcome of test, process the candidates that still
         // apply. Collect a list of blocks where control flow will
@@ -1775,24 +1776,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     );
                     candidate_start
                 } else {
-                    *remainder_start.get_or_insert_with(|| self.cfg.start_new_block())
+                    remainder_start
                 }
             })
             .collect();
 
-        if !candidates.is_empty() {
-            let remainder_start = remainder_start.unwrap_or_else(|| self.cfg.start_new_block());
-            self.match_candidates(
-                span,
-                scrutinee_span,
-                remainder_start,
-                otherwise_block,
-                candidates,
-                fake_borrows,
-            );
-        }
-
-        self.perform_test(span, scrutinee_span, block, &match_place, &test, target_blocks);
+        // Perform the test, branching to one of N blocks.
+        self.perform_test(span, scrutinee_span, start_block, &match_place, &test, target_blocks);
     }
 
     /// Determine the fake borrows that are needed from a set of places that
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 8688c589063..241b6c6cb2c 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -33,7 +33,7 @@ struct UnsafetyVisitor<'a, 'tcx> {
     body_target_features: &'tcx [Symbol],
     /// When inside the LHS of an assignment to a field, this is the type
     /// of the LHS and the span of the assignment expression.
-    assignment_info: Option<(Ty<'tcx>, Span)>,
+    assignment_info: Option<Ty<'tcx>>,
     in_union_destructure: bool,
     param_env: ParamEnv<'tcx>,
     inside_adt: bool,
@@ -473,10 +473,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 if let ty::Adt(adt_def, _) = lhs.ty.kind()
                     && adt_def.is_union()
                 {
-                    if let Some((assigned_ty, assignment_span)) = self.assignment_info {
+                    if let Some(assigned_ty) = self.assignment_info {
                         if assigned_ty.needs_drop(self.tcx, self.param_env) {
-                            // This would be unsafe, but should be outright impossible since we reject such unions.
-                            self.tcx.dcx().span_delayed_bug(assignment_span, format!("union fields that need dropping should be impossible: {assigned_ty}"));
+                            // This would be unsafe, but should be outright impossible since we
+                            // reject such unions.
+                            assert!(
+                                self.tcx.dcx().has_errors().is_some(),
+                                "union fields that need dropping should be impossible: \
+                                {assigned_ty}"
+                            );
                         }
                     } else {
                         self.requires_unsafe(expr.span, AccessToUnionField);
@@ -492,14 +497,15 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                     self.requires_unsafe(expr.span, MutationOfLayoutConstrainedField);
                 }
 
-                // Second, check for accesses to union fields
-                // don't have any special handling for AssignOp since it causes a read *and* write to lhs
+                // Second, check for accesses to union fields. Don't have any
+                // special handling for AssignOp since it causes a read *and*
+                // write to lhs.
                 if matches!(expr.kind, ExprKind::Assign { .. }) {
-                    self.assignment_info = Some((lhs.ty, expr.span));
+                    self.assignment_info = Some(lhs.ty);
                     visit::walk_expr(self, lhs);
                     self.assignment_info = None;
                     visit::walk_expr(self, &self.thir()[rhs]);
-                    return; // we have already visited everything by now
+                    return; // We have already visited everything by now.
                 }
             }
             ExprKind::Borrow { borrow_kind, arg } => {
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index c24b24d6f01..99952e1c178 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -3,6 +3,7 @@ use crate::thir::cx::region::Scope;
 use crate::thir::cx::Cx;
 use crate::thir::util::UserAnnotatedTyHelpers;
 use itertools::Itertools;
+use rustc_ast::LitKind;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
@@ -20,7 +21,8 @@ use rustc_middle::ty::GenericArgs;
 use rustc_middle::ty::{
     self, AdtKind, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, UserType,
 };
-use rustc_span::{sym, Span};
+use rustc_span::source_map::Spanned;
+use rustc_span::{sym, Span, DUMMY_SP};
 use rustc_target::abi::{FieldIdx, FIRST_VARIANT};
 
 impl<'tcx> Cx<'tcx> {
@@ -894,7 +896,14 @@ impl<'tcx> Cx<'tcx> {
             Res::Def(DefKind::ConstParam, def_id) => {
                 let hir_id = self.tcx.local_def_id_to_hir_id(def_id.expect_local());
                 let generics = self.tcx.generics_of(hir_id.owner);
-                let index = generics.param_def_id_to_index[&def_id];
+                let Some(&index) = generics.param_def_id_to_index.get(&def_id) else {
+                    self.tcx.dcx().has_errors().unwrap();
+                    // We already errored about a late bound const
+                    return ExprKind::Literal {
+                        lit: &Spanned { span: DUMMY_SP, node: LitKind::Err },
+                        neg: false,
+                    };
+                };
                 let name = self.tcx.hir().name(hir_id);
                 let param = ty::ParamConst::new(index, name);
 
diff --git a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
index 18a00724c3d..c77c80d9f4b 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/const_to_pat.rs
@@ -153,8 +153,7 @@ impl<'tcx> ConstToPat<'tcx> {
             // a hard error when we don't have a valtree or when we find something in
             // the valtree that is not structural; then this can all be made a lot simpler.
 
-            let structural =
-                traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
+            let structural = traits::search_for_structural_match_violation(self.tcx(), cv.ty());
             debug!(
                 "search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
                 cv.ty(),
diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
index 80e0d0baf57..c6ec1b5aee4 100644
--- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
+++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs
@@ -1,7 +1,7 @@
 use rustc_index::IndexVec;
 use rustc_middle::mir::tcx::{PlaceTy, RvalueInitializationState};
 use rustc_middle::mir::*;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
 use smallvec::{smallvec, SmallVec};
 
 use std::mem;
@@ -132,6 +132,9 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
             let body = self.builder.body;
             let tcx = self.builder.tcx;
             let place_ty = place_ref.ty(body, tcx).ty;
+            if place_ty.references_error() {
+                return MovePathResult::Error;
+            }
             match elem {
                 ProjectionElem::Deref => match place_ty.kind() {
                     ty::Ref(..) | ty::RawPtr(..) => {
diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs
index fbb62695383..a0c3de3af58 100644
--- a/compiler/rustc_mir_transform/src/check_unsafety.rs
+++ b/compiler/rustc_mir_transform/src/check_unsafety.rs
@@ -243,10 +243,11 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
                     // old value is being dropped.
                     let assigned_ty = place.ty(&self.body.local_decls, self.tcx).ty;
                     if assigned_ty.needs_drop(self.tcx, self.param_env) {
-                        // This would be unsafe, but should be outright impossible since we reject such unions.
-                        self.tcx.dcx().span_delayed_bug(
-                            self.source_info.span,
-                            format!("union fields that need dropping should be impossible: {assigned_ty}")
+                        // This would be unsafe, but should be outright impossible since we reject
+                        // such unions.
+                        assert!(
+                            self.tcx.dcx().has_errors().is_some(),
+                            "union fields that need dropping should be impossible: {assigned_ty}"
                         );
                     }
                 } else {
diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs
index d3d0c7bcc95..934e77e7deb 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans.rs
@@ -1,9 +1,10 @@
 use rustc_data_structures::graph::WithNumNodes;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir;
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, Span};
 
 use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
+use crate::coverage::spans::from_mir::SpanFromMir;
 use crate::coverage::ExtractedHirInfo;
 
 mod from_mir;
@@ -61,7 +62,7 @@ pub(super) fn generate_coverage_spans(
             basic_coverage_blocks,
         );
         let coverage_spans = SpansRefiner::refine_sorted_spans(basic_coverage_blocks, sorted_spans);
-        mappings.extend(coverage_spans.into_iter().map(|CoverageSpan { bcb, span, .. }| {
+        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 }
         }));
@@ -85,18 +86,36 @@ pub(super) fn generate_coverage_spans(
     Some(CoverageSpans { bcb_has_mappings, mappings })
 }
 
-/// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that
-/// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s.
-/// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent
-/// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the
-/// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s.
-///
-/// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that
-/// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches
-/// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock`
-/// `dominates()` the `BasicBlock`s in this `CoverageSpan`.
-#[derive(Debug, Clone)]
-struct CoverageSpan {
+#[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,
+}
+
+impl CurrCovspan {
+    fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
+        Self { span, bcb, is_closure }
+    }
+
+    fn into_prev(self) -> PrevCovspan {
+        let Self { span, bcb, is_closure } = self;
+        PrevCovspan { original_span: span, span, bcb, merged_spans: vec![span], is_closure }
+    }
+
+    fn into_refined(self) -> RefinedCovspan {
+        // This is only called in cases where `curr` is a closure span that has
+        // been carved out of `prev`.
+        debug_assert!(self.is_closure);
+        self.into_prev().into_refined()
+    }
+}
+
+#[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
@@ -105,37 +124,82 @@ struct CoverageSpan {
     is_closure: bool,
 }
 
-impl CoverageSpan {
-    fn new(span: Span, bcb: BasicCoverageBlock, is_closure: bool) -> Self {
-        Self { span, bcb, merged_spans: vec![span], is_closure }
+impl PrevCovspan {
+    fn is_mergeable(&self, other: &CurrCovspan) -> bool {
+        self.bcb == other.bcb && !self.is_closure && !other.is_closure
     }
 
-    pub fn merge_from(&mut self, other: &Self) {
+    fn merge_from(&mut self, other: &CurrCovspan) {
         debug_assert!(self.is_mergeable(other));
         self.span = self.span.to(other.span);
-        self.merged_spans.extend_from_slice(&other.merged_spans);
+        self.merged_spans.push(other.span);
     }
 
-    pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
+    fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) {
         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);
         }
     }
 
-    #[inline]
-    pub fn is_mergeable(&self, other: &Self) -> bool {
-        self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure)
+    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 }
     }
 
-    #[inline]
-    pub fn is_in_same_bcb(&self, other: &Self) -> bool {
-        self.bcb == other.bcb
+    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()
     }
 }
 
-/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
-/// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to:
+#[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;
+        RefinedCovspan { span, bcb, is_closure }
+    }
+
+    fn into_refined(self) -> RefinedCovspan {
+        // Even though we consume self, we can just reuse the copying impl.
+        self.refined_copy()
+    }
+}
+
+#[derive(Debug)]
+struct RefinedCovspan {
+    span: Span,
+    bcb: BasicCoverageBlock,
+    is_closure: bool,
+}
+
+impl RefinedCovspan {
+    fn is_mergeable(&self, other: &Self) -> bool {
+        self.bcb == other.bcb && !self.is_closure && !other.is_closure
+    }
+
+    fn merge_from(&mut self, other: &Self) {
+        debug_assert!(self.is_mergeable(other));
+        self.span = self.span.to(other.span);
+    }
+}
+
+/// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a
+/// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to:
 ///
 ///  * Remove duplicate source code coverage regions
 ///  * Merge spans that represent continuous (both in source code and control flow), non-branching
@@ -145,43 +209,33 @@ struct SpansRefiner<'a> {
     /// The BasicCoverageBlock Control Flow Graph (BCB CFG).
     basic_coverage_blocks: &'a CoverageGraph,
 
-    /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative
+    /// 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<CoverageSpan>,
+    sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
 
-    /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the
+    /// The current coverage span to compare to its `prev`, to possibly merge, discard, force the
     /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to
     /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next
     /// iteration.
-    some_curr: Option<CoverageSpan>,
-
-    /// The original `span` for `curr`, in case `curr.span()` is modified. The `curr_original_span`
-    /// **must not be mutated** (except when advancing to the next `curr`), even if `curr.span()`
-    /// is mutated.
-    curr_original_span: Span,
+    some_curr: Option<CurrCovspan>,
 
-    /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`.
+    /// The coverage span from a prior iteration; typically assigned from that iteration's `curr`.
     /// If that `curr` was discarded, `prev` retains its value from the previous iteration.
-    some_prev: Option<CoverageSpan>,
+    some_prev: Option<PrevCovspan>,
 
-    /// Assigned from `curr_original_span` from the previous iteration. The `prev_original_span`
-    /// **must not be mutated** (except when advancing to the next `prev`), even if `prev.span()`
-    /// is mutated.
-    prev_original_span: Span,
-
-    /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and
+    /// 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` `CoverageSpan` moves to `prev` before possibly being added to
+    /// `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<CoverageSpan>,
+    pending_dups: Vec<DuplicateCovspan>,
 
-    /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression`
-    /// will also be injected into the MIR for each `CoverageSpan`.
-    refined_spans: Vec<CoverageSpan>,
+    /// 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> {
@@ -190,15 +244,13 @@ impl<'a> SpansRefiner<'a> {
     /// and carving holes in spans when they overlap in unwanted ways.
     fn refine_sorted_spans(
         basic_coverage_blocks: &'a CoverageGraph,
-        sorted_spans: Vec<CoverageSpan>,
-    ) -> Vec<CoverageSpan> {
+        sorted_spans: Vec<SpanFromMir>,
+    ) -> Vec<RefinedCovspan> {
         let this = Self {
             basic_coverage_blocks,
             sorted_spans_iter: sorted_spans.into_iter(),
             some_curr: None,
-            curr_original_span: DUMMY_SP,
             some_prev: None,
-            prev_original_span: DUMMY_SP,
             pending_dups: Vec::new(),
             refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2),
         };
@@ -206,9 +258,9 @@ impl<'a> SpansRefiner<'a> {
         this.to_refined_spans()
     }
 
-    /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and
-    /// de-duplicated `CoverageSpan`s.
-    fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
+    /// Iterate through the sorted coverage spans, and return the refined list of merged and
+    /// de-duplicated spans.
+    fn to_refined_spans(mut self) -> Vec<RefinedCovspan> {
         while self.next_coverage_span() {
             // For the first span we don't have `prev` set, so most of the
             // span-processing steps don't make sense yet.
@@ -221,16 +273,15 @@ impl<'a> SpansRefiner<'a> {
             let prev = self.prev();
             let curr = self.curr();
 
-            if curr.is_mergeable(prev) {
+            if prev.is_mergeable(curr) {
                 debug!("  same bcb (and neither is a closure), merge with prev={prev:?}");
-                let prev = self.take_prev();
-                self.curr_mut().merge_from(&prev);
-            // Note that curr.span may now differ from curr_original_span
+                let curr = self.take_curr();
+                self.prev_mut().merge_from(&curr);
             } else if prev.span.hi() <= curr.span.lo() {
                 debug!(
                     "  different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}",
                 );
-                let prev = self.take_prev();
+                let prev = self.take_prev().into_refined();
                 self.refined_spans.push(prev);
             } else if prev.is_closure {
                 // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
@@ -241,9 +292,9 @@ impl<'a> SpansRefiner<'a> {
                 self.take_curr(); // Discards curr.
             } else if curr.is_closure {
                 self.carve_out_span_for_closure();
-            } else if self.prev_original_span == curr.span {
-                // `prev` and `curr` have the same span, or would have had the
-                // same span before `prev` was modified by other spans.
+            } 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();
@@ -253,14 +304,14 @@ impl<'a> SpansRefiner<'a> {
         // 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);
+            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() {
             debug!("    AT END, adding last prev={prev:?}");
-            self.refined_spans.push(prev);
+            self.refined_spans.push(prev.into_refined());
         }
 
         // Do one last merge pass, to simplify the output.
@@ -274,7 +325,7 @@ impl<'a> SpansRefiner<'a> {
             }
         });
 
-        // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
+        // Remove spans derived from closures, originally added to ensure the coverage
         // regions for the current function leave room for the closure's own coverage regions
         // (injected separately, from the closure's own MIR).
         self.refined_spans.retain(|covspan| !covspan.is_closure);
@@ -282,34 +333,29 @@ impl<'a> SpansRefiner<'a> {
     }
 
     #[track_caller]
-    fn curr(&self) -> &CoverageSpan {
+    fn curr(&self) -> &CurrCovspan {
         self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))
     }
 
-    #[track_caller]
-    fn curr_mut(&mut self) -> &mut CoverageSpan {
-        self.some_curr.as_mut().unwrap_or_else(|| bug!("some_curr is None (curr_mut)"))
-    }
-
     /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the
     /// `curr` coverage span.
     #[track_caller]
-    fn take_curr(&mut self) -> CoverageSpan {
+    fn take_curr(&mut self) -> CurrCovspan {
         self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)"))
     }
 
     #[track_caller]
-    fn prev(&self) -> &CoverageSpan {
+    fn prev(&self) -> &PrevCovspan {
         self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)"))
     }
 
     #[track_caller]
-    fn prev_mut(&mut self) -> &mut CoverageSpan {
+    fn prev_mut(&mut self) -> &mut PrevCovspan {
         self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)"))
     }
 
     #[track_caller]
-    fn take_prev(&mut self) -> CoverageSpan {
+    fn take_prev(&mut self) -> PrevCovspan {
         self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)"))
     }
 
@@ -335,7 +381,7 @@ impl<'a> SpansRefiner<'a> {
         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);
+                self.refined_spans.push(dup.into_refined());
             }
         } else {
             self.pending_dups.clear();
@@ -343,11 +389,10 @@ impl<'a> SpansRefiner<'a> {
         assert!(self.pending_dups.is_empty());
     }
 
-    /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
+    /// 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() {
-            self.some_prev = Some(curr);
-            self.prev_original_span = self.curr_original_span;
+            self.some_prev = Some(curr.into_prev());
         }
         while let Some(curr) = self.sorted_spans_iter.next() {
             debug!("FOR curr={:?}", curr);
@@ -362,10 +407,7 @@ impl<'a> SpansRefiner<'a> {
                     closure?); prev={prev:?}",
                 );
             } else {
-                // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed
-                // by `self.curr_mut().merge_from(prev)`.
-                self.curr_original_span = curr.span;
-                self.some_curr.replace(curr);
+                self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, curr.is_closure));
                 self.maybe_flush_pending_dups();
                 return true;
             }
@@ -388,11 +430,11 @@ impl<'a> SpansRefiner<'a> {
         let has_post_closure_span = prev.span.hi() > right_cutoff;
 
         if has_pre_closure_span {
-            let mut pre_closure = self.prev().clone();
+            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().cloned() {
+            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);
@@ -402,9 +444,7 @@ impl<'a> SpansRefiner<'a> {
         }
 
         if has_post_closure_span {
-            // Mutate `prev.span()` to start after the closure (and discard curr).
-            // (**NEVER** update `prev_original_span` because it affects the assumptions
-            // about how the `CoverageSpan`s are ordered.)
+            // Mutate `prev.span` to start after the closure (and discard curr).
             self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
             debug!("  Mutated prev.span to start after the closure. prev={:?}", self.prev());
 
@@ -413,25 +453,26 @@ impl<'a> SpansRefiner<'a> {
                 dup.span = dup.span.with_lo(right_cutoff);
             }
 
-            let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev.
+            // 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
+    /// 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()`.
+    /// `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
+    /// 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 `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if
-    /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held,
+    /// 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) {
@@ -439,9 +480,15 @@ impl<'a> SpansRefiner<'a> {
         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 `CoverageSpan`.
+        // 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
@@ -454,42 +501,6 @@ impl<'a> SpansRefiner<'a> {
                 );
             }
         }
-
-        if self.basic_coverage_blocks.dominates(prev_bcb, curr_bcb) {
-            debug!(
-                "  different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}",
-                self.prev()
-            );
-            self.cutoff_prev_at_overlapping_curr();
-        // If one span dominates the other, associate the span with the code from the dominated
-        // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note
-        // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still
-        // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.)
-        //
-        // For example:
-        //     match somenum {
-        //         x if x < 1 => { ... }
-        //     }...
-        //
-        // The span for the first `x` is referenced by both the pattern block (every time it is
-        // evaluated) and the arm code (only when matched). The counter will be applied only to
-        // the dominated block. This allows coverage to track and highlight things like the
-        // assignment of `x` above, if the branch is matched, making `x` available to the arm
-        // code; and to track and highlight the question mark `?` "try" operator at the end of
-        // a function call returning a `Result`, so the `?` is covered when the function returns
-        // an `Err`, and not counted as covered if the function always returns `Ok`.
-        } else {
-            // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.)
-            // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as
-            // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added.
-            debug!(
-                "  different bcbs but SAME spans, and neither dominates, so keep curr for \
-                next iter, and, pending upcoming spans (unless overlapping) add prev={:?}",
-                self.prev()
-            );
-            let prev = self.take_prev();
-            self.pending_dups.push(prev);
-        }
     }
 
     /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_
@@ -512,7 +523,7 @@ impl<'a> SpansRefiner<'a> {
                 debug!("  ... no non-overlapping statements to add");
             } else {
                 debug!("  ... adding modified prev={:?}", self.prev());
-                let prev = self.take_prev();
+                let prev = self.take_prev().into_refined();
                 self.refined_spans.push(prev);
             }
         } else {
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 4ac8dde03a6..9517ede288f 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -9,7 +9,6 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
 use crate::coverage::graph::{
     BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
 };
-use crate::coverage::spans::CoverageSpan;
 use crate::coverage::ExtractedHirInfo;
 
 /// Traverses the MIR body to produce an initial collection of coverage-relevant
@@ -22,7 +21,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
     mir_body: &mir::Body<'_>,
     hir_info: &ExtractedHirInfo,
     basic_coverage_blocks: &CoverageGraph,
-) -> Vec<CoverageSpan> {
+) -> Vec<SpanFromMir> {
     let &ExtractedHirInfo { body_span, .. } = hir_info;
 
     let mut initial_spans = vec![];
@@ -61,7 +60,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
             .then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
     });
 
-    initial_spans.into_iter().map(SpanFromMir::into_coverage_span).collect::<Vec<_>>()
+    initial_spans
 }
 
 /// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
@@ -119,10 +118,10 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
     initial_spans.extend(extra_spans);
 }
 
-// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
-// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
+// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
+// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One coverage span is generated
 // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will
-// merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple
+// merge some coverage spans, at which point a coverage span may represent multiple
 // `Statement`s and/or `Terminator`s.)
 fn bcb_to_initial_coverage_spans<'a, 'tcx>(
     mir_body: &'a mir::Body<'tcx>,
@@ -316,7 +315,7 @@ fn unexpand_into_body_span_with_prev(
 }
 
 #[derive(Debug)]
-struct SpanFromMir {
+pub(super) struct SpanFromMir {
     /// A span that has been extracted from MIR and then "un-expanded" back to
     /// within the current function's `body_span`. After various intermediate
     /// processing steps, this span is emitted as part of the final coverage
@@ -324,10 +323,10 @@ struct SpanFromMir {
     ///
     /// With the exception of `fn_sig_span`, this should always be contained
     /// within `body_span`.
-    span: Span,
+    pub(super) span: Span,
     visible_macro: Option<Symbol>,
-    bcb: BasicCoverageBlock,
-    is_closure: bool,
+    pub(super) bcb: BasicCoverageBlock,
+    pub(super) is_closure: bool,
 }
 
 impl SpanFromMir {
@@ -343,9 +342,4 @@ impl SpanFromMir {
     ) -> Self {
         Self { span, visible_macro, bcb, is_closure }
     }
-
-    fn into_coverage_span(self) -> CoverageSpan {
-        let Self { span, visible_macro: _, bcb, is_closure } = self;
-        CoverageSpan::new(span, bcb, is_closure)
-    }
 }
diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs
index 0ac4ab61d40..2c8201b1903 100644
--- a/compiler/rustc_mir_transform/src/dest_prop.rs
+++ b/compiler/rustc_mir_transform/src/dest_prop.rs
@@ -398,7 +398,8 @@ impl<'alloc> Candidates<'alloc> {
         let candidates = entry.get_mut();
         Self::vec_filter_candidates(p, candidates, f, at);
         if candidates.len() == 0 {
-            entry.remove();
+            // FIXME(#120456) - is `swap_remove` correct?
+            entry.swap_remove();
         }
     }
 
diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 8d4afd5b5dc..370f0db72cb 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -561,9 +561,14 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                         .ok()?;
                     dest.into()
                 }
-                CastKind::FnPtrToPtr
-                | CastKind::PtrToPtr
-                | CastKind::PointerCoercion(
+                CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
+                    let src = self.evaluated[value].as_ref()?;
+                    let src = self.ecx.read_immediate(src).ok()?;
+                    let to = self.ecx.layout_of(to).ok()?;
+                    let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
+                    ret.into()
+                }
+                CastKind::PointerCoercion(
                     ty::adjustment::PointerCoercion::MutToConstPointer
                     | ty::adjustment::PointerCoercion::ArrayToPointer
                     | ty::adjustment::PointerCoercion::UnsafeFnPointer,
@@ -571,8 +576,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
                     let src = self.evaluated[value].as_ref()?;
                     let src = self.ecx.read_immediate(src).ok()?;
                     let to = self.ecx.layout_of(to).ok()?;
-                    let ret = self.ecx.ptr_to_ptr(&src, to).ok()?;
-                    ret.into()
+                    ImmTy::from_immediate(*src, to).into()
                 }
                 _ => return None,
             },
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index 9f30f2836f1..c11fd5fcc90 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -265,7 +265,7 @@ fn mir_const_qualif(tcx: TyCtxt<'_>, def: LocalDefId) -> ConstQualifs {
     let body = &tcx.mir_const(def).borrow();
 
     if body.return_ty().references_error() {
-        tcx.dcx().span_delayed_bug(body.span, "mir_const_qualif: MIR had errors");
+        assert!(tcx.dcx().has_errors().is_some(), "mir_const_qualif: MIR had errors");
         return Default::default();
     }
 
diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
index 42edbeaa622..cd434fecce2 100644
--- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs
+++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs
@@ -1,6 +1,7 @@
 use std::cmp::Ordering;
 
 use rustc_type_ir::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable};
+use rustc_type_ir::visit::TypeVisitableExt;
 use rustc_type_ir::{
     self as ty, Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, ConstTy,
     InferCtxtLike, Interner, IntoKind, PlaceholderLike,
@@ -62,8 +63,8 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> Canonicalizer<'a, Infc
 
         let value = value.fold_with(&mut canonicalizer);
         // FIXME: Restore these assertions. Should we uplift type flags?
-        // assert!(!value.has_infer(), "unexpected infer in {value:?}");
-        // assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
+        assert!(!value.has_infer(), "unexpected infer in {value:?}");
+        assert!(!value.has_placeholders(), "unexpected placeholders in {value:?}");
 
         let (max_universe, variables) = canonicalizer.finalize();
 
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index 7a24b819b5f..445d5b2ce79 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -900,7 +900,7 @@ impl<'a> Parser<'a> {
             // fn foo() -> Foo {
             //     field: value,
             // }
-            info!(?maybe_struct_name, ?self.token);
+            debug!(?maybe_struct_name, ?self.token);
             let mut snapshot = self.create_snapshot_for_diagnostic();
             let path = Path {
                 segments: ThinVec::new(),
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index c1fe8c2133b..17ad08b0569 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -957,8 +957,9 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
     // available as we'd like it to be.
     // FIXME: only remove `libc` when `stdbuild` is active.
     // FIXME: remove special casing for `test`.
-    remaining_lib_features.remove(&sym::libc);
-    remaining_lib_features.remove(&sym::test);
+    // FIXME(#120456) - is `swap_remove` correct?
+    remaining_lib_features.swap_remove(&sym::libc);
+    remaining_lib_features.swap_remove(&sym::test);
 
     /// For each feature in `defined_features`..
     ///
@@ -996,7 +997,8 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
                     unnecessary_stable_feature_lint(tcx, *span, feature, since);
                 }
             }
-            remaining_lib_features.remove(&feature);
+            // FIXME(#120456) - is `swap_remove` correct?
+            remaining_lib_features.swap_remove(&feature);
 
             // `feature` is the feature doing the implying, but `implied_by` is the feature with
             // the attribute that establishes this relationship. `implied_by` is guaranteed to be a
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 3ef9de7da74..8d7c0ca0144 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -4,7 +4,7 @@ use crate::query::plumbing::CycleError;
 use crate::query::DepKind;
 use crate::query::{QueryContext, QueryStackFrame};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{DiagCtxt, Diagnostic, DiagnosticBuilder, Level};
+use rustc_errors::{DiagCtxt, DiagnosticBuilder};
 use rustc_hir::def::DefKind;
 use rustc_session::Session;
 use rustc_span::Span;
@@ -628,15 +628,15 @@ pub fn print_query_stack<Qcx: QueryContext>(
         };
         if Some(count_printed) < num_frames || num_frames.is_none() {
             // Only print to stderr as many stack frames as `num_frames` when present.
-            let mut diag = Diagnostic::new(
-                Level::FailureNote,
-                format!(
-                    "#{} [{:?}] {}",
-                    count_printed, query_info.query.dep_kind, query_info.query.description
-                ),
-            );
-            diag.span = query_info.job.span.into();
-            dcx.force_print_diagnostic(diag);
+            // FIXME: needs translation
+            #[allow(rustc::diagnostic_outside_of_impl)]
+            #[allow(rustc::untranslatable_diagnostic)]
+            dcx.struct_failure_note(format!(
+                "#{} [{:?}] {}",
+                count_printed, query_info.query.dep_kind, query_info.query.description
+            ))
+            .with_span(query_info.job.span)
+            .emit();
             count_printed += 1;
         }
 
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 9158ba00901..754757b5de5 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -429,16 +429,16 @@ where
             let formatter = query.format_value();
             if old_hash != new_hash {
                 // We have an inconsistency. This can happen if one of the two
-                // results is tainted by errors. In this case, delay a bug to
-                // ensure compilation is doomed.
-                qcx.dep_context().sess().dcx().delayed_bug(format!(
+                // results is tainted by errors.
+                assert!(
+                    qcx.dep_context().sess().dcx().has_errors().is_some(),
                     "Computed query value for {:?}({:?}) is inconsistent with fed value,\n\
                         computed={:#?}\nfed={:#?}",
                     query.dep_kind(),
                     key,
                     formatter(&result),
                     formatter(&cached_result),
-                ));
+                );
             }
         }
     }
diff --git a/compiler/rustc_resolve/src/check_unused.rs b/compiler/rustc_resolve/src/check_unused.rs
index fc72d76c3a7..c14788b841d 100644
--- a/compiler/rustc_resolve/src/check_unused.rs
+++ b/compiler/rustc_resolve/src/check_unused.rs
@@ -92,7 +92,8 @@ impl<'a, 'b, 'tcx> UnusedImportCheckVisitor<'a, 'b, 'tcx> {
         } else {
             // This trait import is definitely used, in a way other than
             // method resolution.
-            self.r.maybe_unused_trait_imports.remove(&def_id);
+            // FIXME(#120456) - is `swap_remove` correct?
+            self.r.maybe_unused_trait_imports.swap_remove(&def_id);
             if let Some(i) = self.unused_imports.get_mut(&self.base_id) {
                 i.unused.remove(&id);
             }
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 5d712461993..cb3b95a4434 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -460,7 +460,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             return (err, Vec::new());
         }
 
-        let (found, candidates) = self.try_lookup_name_relaxed(
+        let (found, mut candidates) = self.try_lookup_name_relaxed(
             &mut err,
             source,
             path,
@@ -473,10 +473,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
             return (err, candidates);
         }
 
-        let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
+        if self.suggest_shadowed(&mut err, source, path, following_seg, span) {
+            // if there is already a shadowed name, don'suggest candidates for importing
+            candidates.clear();
+        }
 
-        // if we have suggested using pattern matching, then don't add needless suggestions
-        // for typos.
+        let mut fallback = self.suggest_trait_and_bounds(&mut err, source, res, span, &base_error);
         fallback |= self.suggest_typo(&mut err, source, path, following_seg, span, &base_error);
 
         if fallback {
@@ -872,25 +874,6 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         let ident_span = path.last().map_or(span, |ident| ident.ident.span);
         let typo_sugg =
             self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
-        let is_in_same_file = &|sp1, sp2| {
-            let source_map = self.r.tcx.sess.source_map();
-            let file1 = source_map.span_to_filename(sp1);
-            let file2 = source_map.span_to_filename(sp2);
-            file1 == file2
-        };
-        // print 'you might have meant' if the candidate is (1) is a shadowed name with
-        // accessible definition and (2) either defined in the same crate as the typo
-        // (could be in a different file) or introduced in the same file as the typo
-        // (could belong to a different crate)
-        if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
-            && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))
-        {
-            err.span_label(
-                sugg_span,
-                format!("you might have meant to refer to this {}", res.descr()),
-            );
-            return true;
-        }
         let mut fallback = false;
         let typo_sugg = typo_sugg.to_opt_suggestion();
         if !self.r.add_typo_suggestion(err, typo_sugg, ident_span) {
@@ -918,6 +901,39 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
         fallback
     }
 
+    fn suggest_shadowed(
+        &mut self,
+        err: &mut Diagnostic,
+        source: PathSource<'_>,
+        path: &[Segment],
+        following_seg: Option<&Segment>,
+        span: Span,
+    ) -> bool {
+        let is_expected = &|res| source.is_expected(res);
+        let typo_sugg =
+            self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected);
+        let is_in_same_file = &|sp1, sp2| {
+            let source_map = self.r.tcx.sess.source_map();
+            let file1 = source_map.span_to_filename(sp1);
+            let file2 = source_map.span_to_filename(sp2);
+            file1 == file2
+        };
+        // print 'you might have meant' if the candidate is (1) is a shadowed name with
+        // accessible definition and (2) either defined in the same crate as the typo
+        // (could be in a different file) or introduced in the same file as the typo
+        // (could belong to a different crate)
+        if let TypoCandidate::Shadowed(res, Some(sugg_span)) = typo_sugg
+            && res.opt_def_id().is_some_and(|id| id.is_local() || is_in_same_file(span, sugg_span))
+        {
+            err.span_label(
+                sugg_span,
+                format!("you might have meant to refer to this {}", res.descr()),
+            );
+            return true;
+        }
+        false
+    }
+
     fn err_code_special_cases(
         &mut self,
         err: &mut Diagnostic,
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index d35f951e2ae..a8b9ff056c2 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1435,12 +1435,16 @@ impl CheckCfg {
         //
         // When adding a new config here you should also update
         // `tests/ui/check-cfg/well-known-values.rs`.
+        //
+        // Don't forget to update `src/doc/unstable-book/src/compiler-flags/check-cfg.md`
+        // in the unstable book as well!
 
         ins!(sym::debug_assertions, no_values);
 
-        // These three are never set by rustc, but we set them anyway: they
-        // should not trigger a lint because `cargo doc`, `cargo test`, and
-        // `cargo miri run` (respectively) can set them.
+        // These four are never set by rustc, but we set them anyway: they
+        // should not trigger a lint because `cargo clippy`, `cargo doc`,
+        // `cargo test` and `cargo miri run` (respectively) can set them.
+        ins!(sym::clippy, no_values);
         ins!(sym::doc, no_values);
         ins!(sym::doctest, no_values);
         ins!(sym::miri, no_values);
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 0c084660761..25c20be8e62 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -322,10 +322,9 @@ impl Session {
         }
     }
 
-    /// Used for code paths of expensive computations that should only take place when
-    /// warnings or errors are emitted. If no messages are emitted ("good path"), then
-    /// it's likely a bug.
-    pub fn good_path_delayed_bug(&self, msg: impl Into<DiagnosticMessage>) {
+    /// 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) {
         if self.opts.unstable_opts.print_type_sizes
             || self.opts.unstable_opts.query_dep_graph
             || self.opts.unstable_opts.dump_mir.is_some()
@@ -336,7 +335,7 @@ impl Session {
             return;
         }
 
-        self.dcx().good_path_delayed_bug(msg)
+        self.dcx().set_must_produce_diag()
     }
 
     #[inline]
@@ -546,8 +545,8 @@ impl Session {
                 if fuel.remaining == 0 && !fuel.out_of_fuel {
                     if self.dcx().can_emit_warnings() {
                         // We only call `msg` in case we can actually emit warnings.
-                        // Otherwise, this could cause a `good_path_delayed_bug` to
-                        // trigger (issue #79546).
+                        // Otherwise, this could cause a `must_produce_diag` ICE
+                        // (issue #79546).
                         self.dcx().emit_warn(errors::OptimisationFuelExhausted { msg: msg() });
                     }
                     fuel.out_of_fuel = true;
diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
index c05c9961750..81be5c09164 100644
--- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs
+++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs
@@ -3,33 +3,27 @@
 //! of our more general approach to "lazy normalization".
 //!
 //! This is done by first normalizing both sides of the goal, ending up in
-//! either a concrete type, rigid projection, opaque, or an infer variable.
+//! either a concrete type, rigid alias, or an infer variable.
 //! These are related further according to the rules below:
 //!
-//! (1.) If we end up with a rigid projection and a rigid projection, then we
-//! relate those projections structurally.
+//! (1.) If we end up with two rigid aliases, then we relate them structurally.
 //!
-//! (2.) If we end up with a rigid projection and an alias, then the opaque will
-//! have its hidden type defined to be that rigid projection.
-//!
-//! (3.) If we end up with an opaque and an opaque, then we assemble two
-//! candidates, one defining the LHS to be the hidden type of the RHS, and vice
-//! versa.
-//!
-//! (4.) If we end up with an infer var and an opaque or rigid projection, then
+//! (2.) If we end up with an infer var and a rigid alias, then
 //! we assign the alias to the infer var.
 //!
-//! (5.) If we end up with an opaque and a rigid (non-projection) type, then we
-//! define the hidden type of the opaque to be the rigid type.
-//!
-//! (6.) Otherwise, if we end with two rigid (non-projection) or infer types,
+//! (3.) Otherwise, if we end with two rigid (non-projection) or infer types,
 //! relate them structurally.
+//!
+//! Subtle: when relating an opaque to another type, we emit a
+//! `NormalizesTo(opaque, ?fresh_var)` goal when trying to normalize the opaque.
+//! This nested goal starts out as ambiguous and does not actually define the opaque.
+//! However, if `?fresh_var` ends up geteting equated to another type, we retry the
+//! `NormalizesTo` goal, at which point the opaque is actually defined.
 
 use super::{EvalCtxt, GoalSource};
-use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 
 impl<'tcx> EvalCtxt<'_, 'tcx> {
     #[instrument(level = "debug", skip(self), ret)]
@@ -59,37 +53,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
 
-            (Some(alias), None) => {
+            (Some(_), None) => {
                 if rhs.is_infer() {
                     self.relate(param_env, lhs, variance, rhs)?;
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                } else if alias.is_opaque(tcx) {
-                    // FIXME: This doesn't account for variance.
-                    self.define_opaque(param_env, alias, rhs)
                 } else {
                     Err(NoSolution)
                 }
             }
-            (None, Some(alias)) => {
+            (None, Some(_)) => {
                 if lhs.is_infer() {
                     self.relate(param_env, lhs, variance, rhs)?;
                     self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-                } else if alias.is_opaque(tcx) {
-                    // FIXME: This doesn't account for variance.
-                    self.define_opaque(param_env, alias, lhs)
                 } else {
                     Err(NoSolution)
                 }
             }
 
             (Some(alias_lhs), Some(alias_rhs)) => {
-                self.relate_rigid_alias_or_opaque(param_env, alias_lhs, variance, alias_rhs)
+                self.relate(param_env, alias_lhs, variance, alias_rhs)?;
+                self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
             }
         }
     }
 
     // FIXME: This needs a name that reflects that it's okay to bottom-out with an inference var.
-    /// Normalize the `term` to equate it later. This does not define opaque types.
+    /// Normalize the `term` to equate it later.
     #[instrument(level = "debug", skip(self, param_env), ret)]
     fn try_normalize_term(
         &mut self,
@@ -98,10 +87,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
     ) -> Result<Option<ty::Term<'tcx>>, NoSolution> {
         match term.unpack() {
             ty::TermKind::Ty(ty) => {
-                // We do no define opaque types here but instead do so in `relate_rigid_alias_or_opaque`.
-                Ok(self
-                    .try_normalize_ty_recur(param_env, DefineOpaqueTypes::No, 0, ty)
-                    .map(Into::into))
+                Ok(self.try_normalize_ty_recur(param_env, 0, ty).map(Into::into))
             }
             ty::TermKind::Const(_) => {
                 if let Some(alias) = term.to_alias_ty(self.tcx()) {
@@ -119,51 +105,34 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
-    fn define_opaque(
+    fn try_normalize_ty_recur(
         &mut self,
         param_env: ty::ParamEnv<'tcx>,
-        opaque: ty::AliasTy<'tcx>,
-        term: ty::Term<'tcx>,
-    ) -> QueryResult<'tcx> {
-        self.add_goal(
-            GoalSource::Misc,
-            Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }),
-        );
-        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-    }
-
-    fn relate_rigid_alias_or_opaque(
-        &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        lhs: ty::AliasTy<'tcx>,
-        variance: ty::Variance,
-        rhs: ty::AliasTy<'tcx>,
-    ) -> QueryResult<'tcx> {
-        let tcx = self.tcx();
-        let mut candidates = vec![];
-        if lhs.is_opaque(tcx) {
-            candidates.extend(
-                self.probe_misc_candidate("define-lhs-opaque")
-                    .enter(|ecx| ecx.define_opaque(param_env, lhs, rhs.to_ty(tcx).into())),
-            );
+        depth: usize,
+        ty: Ty<'tcx>,
+    ) -> Option<Ty<'tcx>> {
+        if !self.tcx().recursion_limit().value_within_limit(depth) {
+            return None;
         }
 
-        if rhs.is_opaque(tcx) {
-            candidates.extend(
-                self.probe_misc_candidate("define-rhs-opaque")
-                    .enter(|ecx| ecx.define_opaque(param_env, rhs, lhs.to_ty(tcx).into())),
-            );
-        }
-
-        candidates.extend(self.probe_misc_candidate("args-relate").enter(|ecx| {
-            ecx.relate(param_env, lhs, variance, rhs)?;
-            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
-        }));
+        let ty::Alias(_, alias) = *ty.kind() else {
+            return Some(ty);
+        };
 
-        if let Some(result) = self.try_merge_responses(&candidates) {
-            Ok(result)
-        } else {
-            self.flounder(&candidates)
+        match self.commit_if_ok(|this| {
+            let normalized_ty = this.next_ty_infer();
+            let normalizes_to_goal = Goal::new(
+                this.tcx(),
+                param_env,
+                ty::NormalizesTo { alias, term: normalized_ty.into() },
+            );
+            this.add_goal(GoalSource::Misc, normalizes_to_goal);
+            this.try_evaluate_added_goals()?;
+            let ty = this.resolve_vars_if_possible(normalized_ty);
+            Ok(this.try_normalize_ty_recur(param_env, depth + 1, ty))
+        }) {
+            Ok(ty) => ty,
+            Err(NoSolution) => Some(ty),
         }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 6833d2ae330..00c22e9e2c3 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -276,11 +276,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         &mut self,
         goal: Goal<'tcx, G>,
     ) -> Vec<Candidate<'tcx>> {
-        let Some(normalized_self_ty) =
-            self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
+        let Ok(normalized_self_ty) =
+            self.structurally_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
-            debug!("overflow while evaluating self type");
-            return self.forced_ambiguity(MaybeCause::Overflow);
+            return vec![];
         };
 
         if normalized_self_ty.is_ty_var() {
@@ -338,6 +337,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let mut consider_impls_for_simplified_type = |simp| {
             if let Some(impls_for_type) = trait_impls.non_blanket_impls().get(&simp) {
                 for &impl_def_id in impls_for_type {
+                    // For every `default impl`, there's always a non-default `impl`
+                    // that will *also* apply. There's no reason to register a candidate
+                    // for this impl, since it is *not* proof that the trait goal holds.
+                    if tcx.defaultness(impl_def_id).is_default() {
+                        return;
+                    }
+
                     match G::consider_impl_candidate(self, goal, impl_def_id) {
                         Ok(candidate) => candidates.push(candidate),
                         Err(NoSolution) => (),
@@ -441,6 +447,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let tcx = self.tcx();
         let trait_impls = tcx.trait_impls_of(goal.predicate.trait_def_id(tcx));
         for &impl_def_id in trait_impls.blanket_impls() {
+            // For every `default impl`, there's always a non-default `impl`
+            // that will *also* apply. There's no reason to register a candidate
+            // for this impl, since it is *not* proof that the trait goal holds.
+            if tcx.defaultness(impl_def_id).is_default() {
+                return;
+            }
+
             match G::consider_impl_candidate(self, goal, impl_def_id) {
                 Ok(candidate) => candidates.push(candidate),
                 Err(NoSolution) => (),
@@ -635,19 +648,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
             return;
         }
 
-        match self.try_normalize_ty(goal.param_env, alias_ty.self_ty()) {
-            // Recurse on the self type of the projection.
-            Some(next_self_ty) => {
-                self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates);
-            }
-            // Bail if we overflow when normalizing, adding an ambiguous candidate.
-            None => {
-                if let Ok(result) =
-                    self.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
-                {
-                    candidates.push(Candidate { source: CandidateSource::AliasBound, result });
-                }
+        // Recurse on the self type of the projection.
+        match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) {
+            Ok(next_self_ty) => {
+                self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates)
             }
+            Err(NoSolution) => {}
         }
     }
 
@@ -857,19 +863,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let tcx = self.tcx();
         let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
             let trait_ref = goal.predicate.trait_ref(tcx);
-            #[derive(Debug)]
-            struct Overflow;
-            let lazily_normalize_ty = |ty| match ecx.try_normalize_ty(goal.param_env, ty) {
-                Some(ty) => Ok(ty),
-                None => Err(Overflow),
-            };
+            let lazily_normalize_ty = |ty| ecx.structurally_normalize_ty(goal.param_env, ty);
 
-            match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty) {
-                Err(Overflow) => {
-                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW)
-                }
-                Ok(Ok(())) => Err(NoSolution),
-                Ok(Err(_)) => {
+            match coherence::trait_ref_is_knowable(tcx, trait_ref, lazily_normalize_ty)? {
+                Ok(()) => Err(NoSolution),
+                Err(_) => {
                     ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
                 }
             }
diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs
index a7330136fe7..94a3cef8ad1 100644
--- a/compiler/rustc_trait_selection/src/solve/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/mod.rs
@@ -15,15 +15,13 @@
 //! about it on zulip.
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues};
-use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_infer::traits::query::NoSolution;
 use rustc_middle::infer::canonical::CanonicalVarInfos;
 use rustc_middle::traits::solve::{
     CanonicalResponse, Certainty, ExternalConstraintsData, Goal, GoalSource, IsNormalizesToHack,
     QueryResult, Response,
 };
-use rustc_middle::traits::Reveal;
-use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, UniverseIndex};
+use rustc_middle::ty::{self, AliasRelationDirection, Ty, TyCtxt, UniverseIndex};
 use rustc_middle::ty::{
     CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate,
 };
@@ -267,71 +265,32 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         Ok(self.make_ambiguous_response_no_constraints(maybe_cause))
     }
 
-    /// Normalize a type when it is structually matched on.
+    /// Normalize a type for when it is structurally matched on.
     ///
-    /// In nearly all cases this function must be used before matching on a type.
+    /// This function is necessary in nearly all cases before matching on a type.
     /// Not doing so is likely to be incomplete and therefore unsound during
     /// coherence.
-    #[instrument(level = "debug", skip(self), ret)]
-    fn try_normalize_ty(
-        &mut self,
-        param_env: ty::ParamEnv<'tcx>,
-        ty: Ty<'tcx>,
-    ) -> Option<Ty<'tcx>> {
-        self.try_normalize_ty_recur(param_env, DefineOpaqueTypes::Yes, 0, ty)
-    }
-
-    fn try_normalize_ty_recur(
+    fn structurally_normalize_ty(
         &mut self,
         param_env: ty::ParamEnv<'tcx>,
-        define_opaque_types: DefineOpaqueTypes,
-        depth: usize,
         ty: Ty<'tcx>,
-    ) -> Option<Ty<'tcx>> {
-        if !self.tcx().recursion_limit().value_within_limit(depth) {
-            return None;
-        }
-
-        let ty::Alias(kind, alias) = *ty.kind() else {
-            return Some(ty);
-        };
-
-        // We do no always define opaque types eagerly to allow non-defining uses
-        // in the defining scope. However, if we can unify this opaque to an existing
-        // opaque, then we should attempt to eagerly reveal the opaque, and we fall
-        // through.
-        if let DefineOpaqueTypes::No = define_opaque_types
-            && let Reveal::UserFacing = param_env.reveal()
-            && let ty::Opaque = kind
-            && let Some(def_id) = alias.def_id.as_local()
-            && self.can_define_opaque_ty(def_id)
-        {
-            if self
-                .unify_existing_opaque_tys(
-                    param_env,
-                    OpaqueTypeKey { def_id, args: alias.args },
-                    self.next_ty_infer(),
-                )
-                .is_empty()
-            {
-                return Some(ty);
-            }
-        }
-
-        match self.commit_if_ok(|this| {
-            let normalized_ty = this.next_ty_infer();
-            let normalizes_to_goal = Goal::new(
-                this.tcx(),
+    ) -> Result<Ty<'tcx>, NoSolution> {
+        if let ty::Alias(..) = ty.kind() {
+            let normalized_ty = self.next_ty_infer();
+            let alias_relate_goal = Goal::new(
+                self.tcx(),
                 param_env,
-                ty::NormalizesTo { alias, term: normalized_ty.into() },
+                ty::PredicateKind::AliasRelate(
+                    ty.into(),
+                    normalized_ty.into(),
+                    AliasRelationDirection::Equate,
+                ),
             );
-            this.add_goal(GoalSource::Misc, normalizes_to_goal);
-            this.try_evaluate_added_goals()?;
-            let ty = this.resolve_vars_if_possible(normalized_ty);
-            Ok(this.try_normalize_ty_recur(param_env, define_opaque_types, depth + 1, ty))
-        }) {
-            Ok(ty) => ty,
-            Err(NoSolution) => Some(ty),
+            self.add_goal(GoalSource::Misc, alias_relate_goal);
+            self.try_evaluate_added_goals()?;
+            Ok(self.resolve_vars_if_possible(normalized_ty))
+        } else {
+            Ok(ty)
         }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
index b5d1aa06e4e..356c3776c04 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/opaques.rs
@@ -58,21 +58,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
                     }
                 }
 
-                let expected = match self.try_normalize_ty(goal.param_env, expected) {
-                    Some(ty) => {
-                        if ty.is_ty_var() {
-                            return self.evaluate_added_goals_and_make_canonical_response(
-                                Certainty::AMBIGUOUS,
-                            );
-                        } else {
-                            ty
-                        }
-                    }
-                    None => {
-                        return self
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
-                    }
-                };
+                let expected = self.structurally_normalize_ty(goal.param_env, expected)?;
+                if expected.is_ty_var() {
+                    return self
+                        .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
+                }
 
                 // Otherwise, define a new opaque type
                 self.insert_hidden_type(opaque_type_key, goal.param_env, expected)?;
diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
index 7a466241bfa..eacdd9fde51 100644
--- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs
+++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs
@@ -584,11 +584,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
             let a_ty = goal.predicate.self_ty();
             // We need to normalize the b_ty since it's matched structurally
             // in the other functions below.
-            let b_ty = match ecx
-                .try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))
-            {
-                Some(b_ty) => b_ty,
-                None => return vec![misc_candidate(ecx, Certainty::OVERFLOW)],
+            let Ok(b_ty) = ecx.structurally_normalize_ty(
+                goal.param_env,
+                goal.predicate.trait_ref.args.type_at(1),
+            ) else {
+                return vec![];
             };
 
             let goal = goal.with(ecx.tcx(), (a_ty, b_ty));
diff --git a/compiler/rustc_trait_selection/src/traits/auto_trait.rs b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
index 81c72fc4b7b..fbf96833187 100644
--- a/compiler/rustc_trait_selection/src/traits/auto_trait.rs
+++ b/compiler/rustc_trait_selection/src/traits/auto_trait.rs
@@ -524,13 +524,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                             if let Entry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.remove(&target);
+                                // FIXME(#120456) - is `swap_remove` correct?
+                                smaller_deps.larger.swap_remove(&target);
                             }
 
                             if let Entry::Occupied(v) = vid_map.entry(*larger) {
                                 let larger_deps = v.into_mut();
                                 larger_deps.smaller.insert(*smaller);
-                                larger_deps.smaller.remove(&target);
+                                // FIXME(#120456) - is `swap_remove` correct?
+                                larger_deps.smaller.swap_remove(&target);
                             }
                         }
                         (&RegionTarget::RegionVid(v1), &RegionTarget::Region(r1)) => {
@@ -543,13 +545,15 @@ impl<'tcx> AutoTraitFinder<'tcx> {
                             if let Entry::Occupied(v) = vid_map.entry(*smaller) {
                                 let smaller_deps = v.into_mut();
                                 smaller_deps.larger.insert(*larger);
-                                smaller_deps.larger.remove(&target);
+                                // FIXME(#120456) - is `swap_remove` correct?
+                                smaller_deps.larger.swap_remove(&target);
                             }
 
                             if let Entry::Occupied(v) = vid_map.entry(*larger) {
                                 let larger_deps = v.into_mut();
                                 larger_deps.smaller.insert(*smaller);
-                                larger_deps.smaller.remove(&target);
+                                // FIXME(#120456) - is `swap_remove` correct?
+                                larger_deps.smaller.swap_remove(&target);
                             }
                         }
                     }
diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
index 522c645253a..1edf6b11fc3 100644
--- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
+++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs
@@ -62,9 +62,10 @@ 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.
+                // 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",
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 efaea4e54dc..73effb33560 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
@@ -236,9 +236,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             }
         }
 
-        // It could be that we don't report an error because we have seen an `ErrorReported` from another source.
-        // We should probably be able to fix most of these, but some are delayed bugs that get a proper error
-        // after this function.
+        // It could be that we don't report an error because we have seen an `ErrorReported` from
+        // another source. We should probably be able to fix most of these, but some are delayed
+        // bugs that get a proper error after this function.
         reported.unwrap_or_else(|| self.dcx().delayed_bug("failed to report fulfillment errors"))
     }
 
@@ -519,7 +519,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                                 trait_ref,
                                 span,
                             ) {
-                                GetSafeTransmuteErrorAndReason::Silent => return self.dcx().span_delayed_bug(span, "silent safe transmute error"),
+                                GetSafeTransmuteErrorAndReason::Silent => {
+                                    return self.dcx().span_delayed_bug(
+                                        span, "silent safe transmute error"
+                                    );
+                                }
                                 GetSafeTransmuteErrorAndReason::Error {
                                     err_msg,
                                     safe_transmute_explanation,
@@ -3112,10 +3116,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             obligation.param_env,
             trait_ref.args.const_at(3),
         ) else {
-            span_bug!(
+            self.dcx().span_delayed_bug(
                 span,
-                "Unable to construct rustc_transmute::Assume where it was previously possible"
+                "Unable to construct rustc_transmute::Assume where it was previously possible",
             );
+            return GetSafeTransmuteErrorAndReason::Silent;
         };
 
         match rustc_transmute::TransmuteTypeEnv::new(self.infcx).is_transmutable(
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index 29a4a078fe0..7b715984c2b 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -555,7 +555,7 @@ fn virtual_call_violations_for_method<'tcx>(
 
     // NOTE: This check happens last, because it results in a lint, and not a
     // hard error.
-    if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| {
+    if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, _span)| {
         // dyn Trait is okay:
         //
         //     trait Trait {
@@ -594,7 +594,10 @@ fn virtual_call_violations_for_method<'tcx>(
             // would already have reported an error at the definition of the
             // auto trait.
             if pred_trait_ref.args.len() != 1 {
-                tcx.dcx().span_delayed_bug(span, "auto traits cannot have generic parameters");
+                assert!(
+                    tcx.dcx().has_errors().is_some(),
+                    "auto traits cannot have generic parameters"
+                );
             }
             return false;
         }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index 31a0d6271fa..149dc4c75a7 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -566,6 +566,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 {
                     return;
                 }
+
+                // For every `default impl`, there's always a non-default `impl`
+                // that will *also* apply. There's no reason to register a candidate
+                // for this impl, since it is *not* proof that the trait goal holds.
+                if self.tcx().defaultness(impl_def_id).is_default() {
+                    return;
+                }
+
                 if self.reject_fn_ptr_impls(
                     impl_def_id,
                     obligation,
diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
index 2ce33a4d122..e4a70c537d2 100644
--- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs
@@ -192,13 +192,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             &mut obligations,
         );
 
-        obligations.extend(self.infcx.commit_if_ok(|_| {
+        obligations.extend(
             self.infcx
                 .at(&obligation.cause, obligation.param_env)
                 .sup(DefineOpaqueTypes::No, placeholder_trait_predicate, candidate)
                 .map(|InferOk { obligations, .. }| obligations)
-                .map_err(|_| Unimplemented)
-        })?);
+                .map_err(|_| Unimplemented)?,
+        );
 
         // FIXME(compiler-errors): I don't think this is needed.
         if let ty::Alias(ty::Projection, alias_ty) = placeholder_self_ty.kind() {
@@ -532,13 +532,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             &mut nested,
         );
 
-        nested.extend(self.infcx.commit_if_ok(|_| {
+        nested.extend(
             self.infcx
                 .at(&obligation.cause, obligation.param_env)
                 .sup(DefineOpaqueTypes::No, obligation_trait_ref, upcast_trait_ref)
                 .map(|InferOk { obligations, .. }| obligations)
-                .map_err(|_| Unimplemented)
-        })?);
+                .map_err(|_| Unimplemented)?,
+        );
 
         // Check supertraits hold. This is so that their associated type bounds
         // will be checked in the code below.
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 185b735594f..ab0c53e6a9b 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1605,6 +1605,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         on_ambiguity: impl FnOnce(),
     ) -> ControlFlow<T, ()> {
         let mut idx = 0;
+        let mut in_parent_alias_type = false;
+
         loop {
             let (kind, alias_ty) = match *self_ty.kind() {
                 ty::Alias(kind @ (ty::Projection | ty::Opaque), alias_ty) => (kind, alias_ty),
@@ -1618,6 +1620,18 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             for bound in
                 self.tcx().item_bounds(alias_ty.def_id).instantiate(self.tcx(), alias_ty.args)
             {
+                // HACK: On subsequent recursions, we only care about bounds that don't
+                // share the same type as `self_ty`. This is because for truly rigid
+                // projections, we will never be able to equate, e.g. `<T as Tr>::A`
+                // with `<<T as Tr>::A as Tr>::A`.
+                if in_parent_alias_type {
+                    match bound.kind().skip_binder() {
+                        ty::ClauseKind::Trait(tr) if tr.self_ty() == self_ty => continue,
+                        ty::ClauseKind::Projection(p) if p.self_ty() == self_ty => continue,
+                        _ => {}
+                    }
+                }
+
                 for_each(self, bound, idx)?;
                 idx += 1;
             }
@@ -1627,6 +1641,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             } else {
                 return ControlFlow::Continue(());
             }
+
+            in_parent_alias_type = true;
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 73df1be6b55..e1a49ecf1b6 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -522,7 +522,8 @@ pub(crate) fn to_pretty_impl_header(tcx: TyCtxt<'_>, impl_def_id: DefId) -> Opti
     for (p, _) in predicates {
         if let Some(poly_trait_ref) = p.as_trait_clause() {
             if Some(poly_trait_ref.def_id()) == sized_trait {
-                types_without_default_bounds.remove(&poly_trait_ref.self_ty().skip_binder());
+                // FIXME(#120456) - is `swap_remove` correct?
+                types_without_default_bounds.swap_remove(&poly_trait_ref.self_ty().skip_binder());
                 continue;
             }
         }
diff --git a/compiler/rustc_trait_selection/src/traits/structural_match.rs b/compiler/rustc_trait_selection/src/traits/structural_match.rs
index 89459f377dd..e6b42f15d51 100644
--- a/compiler/rustc_trait_selection/src/traits/structural_match.rs
+++ b/compiler/rustc_trait_selection/src/traits/structural_match.rs
@@ -1,7 +1,6 @@
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
-use rustc_span::Span;
 use std::ops::ControlFlow;
 
 /// This method traverses the structure of `ty`, trying to find an
@@ -30,19 +29,16 @@ use std::ops::ControlFlow;
 /// that arose when the requirement was not enforced completely, see
 /// Rust RFC 1445, rust-lang/rust#61188, and rust-lang/rust#62307.
 pub fn search_for_structural_match_violation<'tcx>(
-    span: Span,
     tcx: TyCtxt<'tcx>,
     ty: Ty<'tcx>,
 ) -> Option<Ty<'tcx>> {
-    ty.visit_with(&mut Search { tcx, span, seen: FxHashSet::default() }).break_value()
+    ty.visit_with(&mut Search { tcx, seen: FxHashSet::default() }).break_value()
 }
 
 /// This implements the traversal over the structure of a given type to try to
 /// find instances of ADTs (specifically structs or enums) that do not implement
 /// `StructuralPartialEq`.
 struct Search<'tcx> {
-    span: Span,
-
     tcx: TyCtxt<'tcx>,
 
     /// Tracks ADTs previously encountered during search, so that
@@ -138,7 +134,6 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for Search<'tcx> {
                 bug!("unexpected type during structural-match checking: {:?}", ty);
             }
             ty::Error(_) => {
-                self.tcx.dcx().span_delayed_bug(self.span, "ty::Error in structural-match check");
                 // We still want to check other types after encountering an error,
                 // as this may still emit relevant errors.
                 return ControlFlow::Continue(());
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
new file mode 100644
index 00000000000..57f961ac97e
--- /dev/null
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -0,0 +1,7 @@
+use crate::Interner;
+
+pub trait BoundVars<I: Interner> {
+    fn bound_vars(&self) -> I::BoundVars;
+
+    fn has_no_bound_vars(&self) -> bool;
+}
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index 188910ecc52..7728ee0e842 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -2,9 +2,10 @@ use smallvec::SmallVec;
 use std::fmt::Debug;
 use std::hash::Hash;
 
+use crate::visit::{Flags, TypeSuperVisitable, TypeVisitable};
 use crate::{
-    BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, TyKind,
-    UniverseIndex,
+    BoundVar, BoundVars, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind,
+    TyKind, UniverseIndex,
 };
 
 pub trait Interner: Sized {
@@ -19,7 +20,10 @@ pub trait Interner: Sized {
     type GenericArg: Copy + DebugWithInfcx<Self> + Hash + Ord;
     type Term: Copy + Debug + Hash + Ord;
 
-    type Binder<T>;
+    type Binder<T: TypeVisitable<Self>>: BoundVars<Self> + TypeSuperVisitable<Self>;
+    type BoundVars: IntoIterator<Item = Self::BoundVar>;
+    type BoundVar;
+
     type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
 
     // Kinds of tys
@@ -28,7 +32,9 @@ pub trait Interner: Sized {
         + Hash
         + Ord
         + Into<Self::GenericArg>
-        + IntoKind<Kind = TyKind<Self>>;
+        + IntoKind<Kind = TyKind<Self>>
+        + TypeSuperVisitable<Self>
+        + Flags;
     type Tys: Copy + Debug + Hash + Ord + IntoIterator<Item = Self::Ty>;
     type AliasTy: Copy + DebugWithInfcx<Self> + Hash + Ord;
     type ParamTy: Copy + Debug + Hash + Ord;
@@ -48,7 +54,9 @@ pub trait Interner: Sized {
         + Ord
         + Into<Self::GenericArg>
         + IntoKind<Kind = ConstKind<Self>>
-        + ConstTy<Self>;
+        + ConstTy<Self>
+        + TypeSuperVisitable<Self>
+        + Flags;
     type AliasConst: Copy + DebugWithInfcx<Self> + Hash + Ord;
     type PlaceholderConst: Copy + Debug + Hash + Ord + PlaceholderLike;
     type ParamConst: Copy + Debug + Hash + Ord;
@@ -62,7 +70,8 @@ pub trait Interner: Sized {
         + Hash
         + Ord
         + Into<Self::GenericArg>
-        + IntoKind<Kind = RegionKind<Self>>;
+        + IntoKind<Kind = RegionKind<Self>>
+        + Flags;
     type EarlyParamRegion: Copy + Debug + Hash + Ord;
     type LateParamRegion: Copy + Debug + Hash + Ord;
     type BoundRegion: Copy + Debug + Hash + Ord;
@@ -70,7 +79,7 @@ pub trait Interner: Sized {
     type PlaceholderRegion: Copy + Debug + Hash + Ord + PlaceholderLike;
 
     // Predicates
-    type Predicate: Copy + Debug + Hash + Eq;
+    type Predicate: Copy + Debug + Hash + Eq + TypeSuperVisitable<Self> + Flags;
     type TraitPredicate: Copy + Debug + Hash + Eq;
     type RegionOutlivesPredicate: Copy + Debug + Hash + Eq;
     type TypeOutlivesPredicate: Copy + Debug + Hash + Eq;
@@ -86,6 +95,9 @@ 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/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs
index f498c5531fc..94ccbcbd8a5 100644
--- a/compiler/rustc_type_ir/src/lib.rs
+++ b/compiler/rustc_type_ir/src/lib.rs
@@ -30,6 +30,7 @@ pub mod visit;
 
 #[macro_use]
 mod macros;
+mod binder;
 mod canonical;
 mod const_kind;
 mod debug;
@@ -39,6 +40,7 @@ mod interner;
 mod predicate_kind;
 mod region_kind;
 
+pub use binder::*;
 pub use canonical::*;
 #[cfg(feature = "nightly")]
 pub use codec::*;
diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs
index 7aa99004667..638fb9f7fa9 100644
--- a/compiler/rustc_type_ir/src/visit.rs
+++ b/compiler/rustc_type_ir/src/visit.rs
@@ -45,8 +45,7 @@ use rustc_index::{Idx, IndexVec};
 use std::fmt;
 use std::ops::ControlFlow;
 
-use crate::Interner;
-use crate::Lrc;
+use crate::{self as ty, BoundVars, Interner, IntoKind, Lrc, TypeFlags};
 
 /// This trait is implemented for every type that can be visited,
 /// providing the skeleton of the traversal.
@@ -88,38 +87,28 @@ pub trait TypeVisitor<I: Interner>: Sized {
     #[cfg(not(feature = "nightly"))]
     type BreakTy;
 
-    fn visit_binder<T: TypeVisitable<I>>(&mut self, t: &I::Binder<T>) -> ControlFlow<Self::BreakTy>
-    where
-        I::Binder<T>: TypeSuperVisitable<I>,
-    {
+    fn visit_binder<T: TypeVisitable<I>>(
+        &mut self,
+        t: &I::Binder<T>,
+    ) -> ControlFlow<Self::BreakTy> {
         t.super_visit_with(self)
     }
 
-    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy>
-    where
-        I::Ty: TypeSuperVisitable<I>,
-    {
+    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
         t.super_visit_with(self)
     }
 
     // The default region visitor is a no-op because `Region` is non-recursive
-    // and has no `super_visit_with` method to call. That also explains the
-    // lack of `I::Region: TypeSuperVisitable<I>` bound.
+    // and has no `super_visit_with` method to call.
     fn visit_region(&mut self, _r: I::Region) -> ControlFlow<Self::BreakTy> {
         ControlFlow::Continue(())
     }
 
-    fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy>
-    where
-        I::Const: TypeSuperVisitable<I>,
-    {
+    fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> {
         c.super_visit_with(self)
     }
 
-    fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy>
-    where
-        I::Predicate: TypeSuperVisitable<I>,
-    {
+    fn visit_predicate(&mut self, p: I::Predicate) -> ControlFlow<Self::BreakTy> {
         p.super_visit_with(self)
     }
 }
@@ -200,3 +189,363 @@ impl<I: Interner, T: TypeVisitable<I>, Ix: Idx> TypeVisitable<I> for IndexVec<Ix
         self.iter().try_for_each(|t| t.visit_with(visitor))
     }
 }
+
+pub trait Flags {
+    fn flags(&self) -> TypeFlags;
+    fn outer_exclusive_binder(&self) -> ty::DebruijnIndex;
+}
+
+pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> {
+    fn has_type_flags(&self, flags: TypeFlags) -> bool;
+
+    /// Returns `true` if `self` has any late-bound regions that are either
+    /// bound by `binder` or bound by some binder outside of `binder`.
+    /// If `binder` is `ty::INNERMOST`, this indicates whether
+    /// there are any late-bound regions that appear free.
+    fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool;
+
+    /// Returns `true` if this type has any regions that escape `binder` (and
+    /// hence are not bound by it).
+    fn has_vars_bound_above(&self, binder: ty::DebruijnIndex) -> bool {
+        self.has_vars_bound_at_or_above(binder.shifted_in(1))
+    }
+
+    /// Return `true` if this type has regions that are not a part of the type.
+    /// For example, `for<'a> fn(&'a i32)` return `false`, while `fn(&'a i32)`
+    /// would return `true`. The latter can occur when traversing through the
+    /// former.
+    ///
+    /// See [`HasEscapingVarsVisitor`] for more information.
+    fn has_escaping_bound_vars(&self) -> bool {
+        self.has_vars_bound_at_or_above(ty::INNERMOST)
+    }
+
+    fn has_projections(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PROJECTION)
+    }
+
+    fn has_inherent_projections(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_INHERENT)
+    }
+
+    fn has_opaque_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_OPAQUE)
+    }
+
+    fn has_coroutines(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_COROUTINE)
+    }
+
+    fn references_error(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_ERROR)
+    }
+
+    fn error_reported(&self) -> Result<(), I::ErrorGuaranteed>;
+
+    fn has_non_region_param(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PARAM - TypeFlags::HAS_RE_PARAM)
+    }
+
+    fn has_infer_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_RE_INFER)
+    }
+
+    fn has_infer_types(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_TY_INFER)
+    }
+
+    fn has_non_region_infer(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_INFER - TypeFlags::HAS_RE_INFER)
+    }
+
+    fn has_infer(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_INFER)
+    }
+
+    fn has_placeholders(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PLACEHOLDER)
+    }
+
+    fn has_non_region_placeholders(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER)
+    }
+
+    fn has_param(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_PARAM)
+    }
+
+    /// "Free" regions in this context means that it has any region
+    /// that is not (a) erased or (b) late-bound.
+    fn has_free_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
+    }
+
+    fn has_erased_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_RE_ERASED)
+    }
+
+    /// True if there are any un-erased free regions.
+    fn has_erasable_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_FREE_REGIONS)
+    }
+
+    /// Indicates whether this value references only 'global'
+    /// generic parameters that are the same regardless of what fn we are
+    /// in. This is used for caching.
+    fn is_global(&self) -> bool {
+        !self.has_type_flags(TypeFlags::HAS_FREE_LOCAL_NAMES)
+    }
+
+    /// True if there are any late-bound regions
+    fn has_bound_regions(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_RE_BOUND)
+    }
+    /// True if there are any late-bound non-region variables
+    fn has_non_region_bound_vars(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_BOUND_VARS - TypeFlags::HAS_RE_BOUND)
+    }
+    /// True if there are any bound variables
+    fn has_bound_vars(&self) -> bool {
+        self.has_type_flags(TypeFlags::HAS_BOUND_VARS)
+    }
+
+    /// Indicates whether this value still has parameters/placeholders/inference variables
+    /// which could be replaced later, in a way that would change the results of `impl`
+    /// specialization.
+    fn still_further_specializable(&self) -> bool {
+        self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
+    }
+}
+
+impl<I: Interner, T: TypeVisitable<I>> TypeVisitableExt<I> for T {
+    fn has_type_flags(&self, flags: TypeFlags) -> bool {
+        let res =
+            self.visit_with(&mut HasTypeFlagsVisitor { flags }) == ControlFlow::Break(FoundFlags);
+        res
+    }
+
+    fn has_vars_bound_at_or_above(&self, binder: ty::DebruijnIndex) -> bool {
+        self.visit_with(&mut HasEscapingVarsVisitor { outer_index: binder }).is_break()
+    }
+
+    fn error_reported(&self) -> Result<(), I::ErrorGuaranteed> {
+        if self.references_error() {
+            if let ControlFlow::Break(guar) = self.visit_with(&mut HasErrorVisitor) {
+                Err(guar)
+            } else {
+                panic!("type flags said there was an error, but now there is not")
+            }
+        } else {
+            Ok(())
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+struct FoundFlags;
+
+// FIXME: Optimize for checking for infer flags
+struct HasTypeFlagsVisitor {
+    flags: ty::TypeFlags,
+}
+
+impl std::fmt::Debug for HasTypeFlagsVisitor {
+    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        self.flags.fmt(fmt)
+    }
+}
+
+// Note: this visitor traverses values down to the level of
+// `Ty`/`Const`/`Predicate`, but not within those types. This is because the
+// type flags at the outer layer are enough. So it's faster than it first
+// looks, particular for `Ty`/`Predicate` where it's just a field access.
+//
+// N.B. The only case where this isn't totally true is binders, which also
+// add `HAS_{RE,TY,CT}_LATE_BOUND` flag depending on the *bound variables* that
+// are present, regardless of whether those bound variables are used. This
+// is important for anonymization of binders in `TyCtxt::erase_regions`. We
+// specifically detect this case in `visit_binder`.
+impl<I: Interner> TypeVisitor<I> for HasTypeFlagsVisitor {
+    type BreakTy = FoundFlags;
+
+    fn visit_binder<T: TypeVisitable<I>>(
+        &mut self,
+        t: &I::Binder<T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        // If we're looking for the HAS_BINDER_VARS flag, check if the
+        // binder has vars. This won't be present in the binder's bound
+        // value, so we need to check here too.
+        if self.flags.intersects(TypeFlags::HAS_BINDER_VARS) && !t.has_no_bound_vars() {
+            return ControlFlow::Break(FoundFlags);
+        }
+
+        t.super_visit_with(self)
+    }
+
+    #[inline]
+    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
+        let flags = t.flags();
+        if flags.intersects(self.flags) {
+            ControlFlow::Break(FoundFlags)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    #[inline]
+    fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call, as usual for `Region`.
+        let flags = r.flags();
+        if flags.intersects(self.flags) {
+            ControlFlow::Break(FoundFlags)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    #[inline]
+    fn visit_const(&mut self, c: I::Const) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
+        if c.flags().intersects(self.flags) {
+            ControlFlow::Break(FoundFlags)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    #[inline]
+    fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
+        // Note: no `super_visit_with` call.
+        if predicate.flags().intersects(self.flags) {
+            ControlFlow::Break(FoundFlags)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+struct FoundEscapingVars;
+
+/// An "escaping var" is a bound var whose binder is not part of `t`. A bound var can be a
+/// bound region or a bound type.
+///
+/// So, for example, consider a type like the following, which has two binders:
+///
+///    for<'a> fn(x: for<'b> fn(&'a isize, &'b isize))
+///    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ outer scope
+///                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~  inner scope
+///
+/// This type has *bound regions* (`'a`, `'b`), but it does not have escaping regions, because the
+/// binders of both `'a` and `'b` are part of the type itself. However, if we consider the *inner
+/// fn type*, that type has an escaping region: `'a`.
+///
+/// Note that what I'm calling an "escaping var" is often just called a "free var". However,
+/// we already use the term "free var". It refers to the regions or types that we use to represent
+/// bound regions or type params on a fn definition while we are type checking its body.
+///
+/// To clarify, conceptually there is no particular difference between
+/// an "escaping" var and a "free" var. However, there is a big
+/// difference in practice. Basically, when "entering" a binding
+/// level, one is generally required to do some sort of processing to
+/// a bound var, such as replacing it with a fresh/placeholder
+/// var, or making an entry in the environment to represent the
+/// scope to which it is attached, etc. An escaping var represents
+/// a bound var for which this processing has not yet been done.
+struct HasEscapingVarsVisitor {
+    /// Anything bound by `outer_index` or "above" is escaping.
+    outer_index: ty::DebruijnIndex,
+}
+
+impl<I: Interner> TypeVisitor<I> for HasEscapingVarsVisitor {
+    type BreakTy = FoundEscapingVars;
+
+    fn visit_binder<T: TypeVisitable<I>>(
+        &mut self,
+        t: &I::Binder<T>,
+    ) -> ControlFlow<Self::BreakTy> {
+        self.outer_index.shift_in(1);
+        let result = t.super_visit_with(self);
+        self.outer_index.shift_out(1);
+        result
+    }
+
+    #[inline]
+    fn visit_ty(&mut self, t: I::Ty) -> ControlFlow<Self::BreakTy> {
+        // If the outer-exclusive-binder is *strictly greater* than
+        // `outer_index`, that means that `t` contains some content
+        // bound at `outer_index` or above (because
+        // `outer_exclusive_binder` is always 1 higher than the
+        // content in `t`). Therefore, `t` has some escaping vars.
+        if t.outer_exclusive_binder() > self.outer_index {
+            ControlFlow::Break(FoundEscapingVars)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    #[inline]
+    fn visit_region(&mut self, r: I::Region) -> ControlFlow<Self::BreakTy> {
+        // If the region is bound by `outer_index` or anything outside
+        // of outer index, then it escapes the binders we have
+        // visited.
+        if r.outer_exclusive_binder() > self.outer_index {
+            ControlFlow::Break(FoundEscapingVars)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    fn visit_const(&mut self, ct: I::Const) -> ControlFlow<Self::BreakTy> {
+        // If the outer-exclusive-binder is *strictly greater* than
+        // `outer_index`, that means that `ct` contains some content
+        // bound at `outer_index` or above (because
+        // `outer_exclusive_binder` is always 1 higher than the
+        // content in `t`). Therefore, `t` has some escaping vars.
+        if ct.outer_exclusive_binder() > self.outer_index {
+            ControlFlow::Break(FoundEscapingVars)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+
+    #[inline]
+    fn visit_predicate(&mut self, predicate: I::Predicate) -> ControlFlow<Self::BreakTy> {
+        if predicate.outer_exclusive_binder() > self.outer_index {
+            ControlFlow::Break(FoundEscapingVars)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
+struct HasErrorVisitor;
+
+impl<I: Interner> TypeVisitor<I> for HasErrorVisitor {
+    type BreakTy = I::ErrorGuaranteed;
+
+    fn visit_ty(&mut self, t: <I as Interner>::Ty) -> ControlFlow<Self::BreakTy> {
+        if let ty::Error(guar) = t.kind() {
+            ControlFlow::Break(guar)
+        } else {
+            t.super_visit_with(self)
+        }
+    }
+
+    fn visit_const(&mut self, c: <I as Interner>::Const) -> ControlFlow<Self::BreakTy> {
+        if let ty::ConstKind::Error(guar) = c.kind() {
+            ControlFlow::Break(guar)
+        } else {
+            c.super_visit_with(self)
+        }
+    }
+
+    fn visit_region(&mut self, r: <I as Interner>::Region) -> ControlFlow<Self::BreakTy> {
+        if let ty::ReError(guar) = r.kind() {
+            ControlFlow::Break(guar)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}