diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 95ea42b584a..103c7ed8ef7 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -299,7 +299,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         if errors.is_empty() {
             definition_ty
         } else {
-            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            infcx.err_ctxt().report_fulfillment_errors(&errors, None);
             self.tcx.ty_error()
         }
     }
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/check.rs b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
index b1ad22b899e..5a8b3e30b9f 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/check.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/check.rs
@@ -765,7 +765,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
 
                         let errors = ocx.select_all_or_error();
                         if !errors.is_empty() {
-                            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+                            infcx.err_ctxt().report_fulfillment_errors(&errors, None);
                         }
                     }
 
@@ -831,7 +831,6 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> {
                                         obligation.clone(),
                                         &obligation,
                                         &e,
-                                        false,
                                     );
                                 }
 
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
index 133bbd52b91..0ba5e615101 100644
--- a/compiler/rustc_hir_analysis/src/check/check.rs
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -26,6 +26,7 @@ use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVE
 use rustc_span::symbol::sym;
 use rustc_span::{self, Span};
 use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
 use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::{self, ObligationCtxt};
 
@@ -471,7 +472,7 @@ fn check_opaque_meets_bounds<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
     }
     match origin {
         // Checked when type checking the function containing them.
@@ -655,7 +656,7 @@ fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
 
 pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
     // an error would be reported if this fails.
-    let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
+    let _ = OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
 }
 
 pub(super) fn check_specialization_validity<'tcx>(
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
index c6b497e9b9f..0e8ac17fb71 100644
--- a/compiler/rustc_hir_analysis/src/check/compare_method.rs
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -405,7 +405,7 @@ fn compare_predicate_entailment<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return Err(reported);
     }
 
@@ -538,7 +538,7 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
     // RPITs.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return Err(reported);
     }
 
@@ -1431,7 +1431,7 @@ pub(crate) fn raw_compare_const_impl<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
+        return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None));
     }
 
     // FIXME return `ErrorReported` if region obligations error?
@@ -1549,7 +1549,7 @@ fn compare_type_predicate_entailment<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return Err(reported);
     }
 
@@ -1769,7 +1769,7 @@ pub fn check_type_bounds<'tcx>(
     // version.
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return Err(reported);
     }
 
diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 416b555db5c..e2c967d0b08 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -105,7 +105,7 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
     f(&mut wfcx);
     let errors = wfcx.select_all_or_error();
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
index b6c91d425df..6f74ef3ccad 100644
--- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -321,7 +321,7 @@ fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did:
                     }),
                 );
                 if !errors.is_empty() {
-                    infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+                    infcx.err_ctxt().report_fulfillment_errors(&errors, None);
                 }
 
                 // Finally, resolve all regions.
@@ -561,7 +561,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
         predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, source, &[target.into()]);
     let errors = traits::fully_solve_obligation(&infcx, predicate);
     if !errors.is_empty() {
-        infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        infcx.err_ctxt().report_fulfillment_errors(&errors, None);
     }
 
     // Finally, resolve all regions.
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index e806e94879d..267077cdab4 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -155,7 +155,7 @@ fn get_impl_substs<'tcx>(
 
     let errors = ocx.select_all_or_error();
     if !errors.is_empty() {
-        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+        ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None);
         return None;
     }
 
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index bd1a461b935..664d3a3a1db 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -173,7 +173,7 @@ fn require_same_types<'tcx>(
     match &errors[..] {
         [] => true,
         errors => {
-            infcx.err_ctxt().report_fulfillment_errors(errors, None, false);
+            infcx.err_ctxt().report_fulfillment_errors(errors, None);
             false
         }
     }
@@ -336,7 +336,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
         ocx.register_bound(cause, param_env, norm_return_ty, term_did);
         let errors = ocx.select_all_or_error();
         if !errors.is_empty() {
-            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            infcx.err_ctxt().report_fulfillment_errors(&errors, None);
             error = true;
         }
         // now we can take the return type of the given main function
diff --git a/compiler/rustc_hir_typeck/src/_match.rs b/compiler/rustc_hir_typeck/src/_match.rs
index 8d39fa81165..6a4a6a5b0a5 100644
--- a/compiler/rustc_hir_typeck/src/_match.rs
+++ b/compiler/rustc_hir_typeck/src/_match.rs
@@ -1,6 +1,6 @@
 use crate::coercion::{AsCoercionSite, CoerceMany};
 use crate::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_errors::{Applicability, MultiSpan};
+use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::Obligation;
@@ -137,55 +137,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Some(&arm.body),
                 arm_ty,
                 Some(&mut |err| {
-                    let Some(ret) = self
-                        .tcx
-                        .hir()
-                        .find_by_def_id(self.body_id.owner.def_id)
-                        .and_then(|owner| owner.fn_decl())
-                        .map(|decl| decl.output.span())
-                    else { return; };
-                    let Expectation::IsLast(stmt) = orig_expected else {
-                        return
-                    };
-                    let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
-                        Some(ret_coercion) if self.in_tail_expr => {
-                            let ret_ty = ret_coercion.borrow().expected_ty();
-                            let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
-                            self.can_coerce(arm_ty, ret_ty)
-                                && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
-                                // The match arms need to unify for the case of `impl Trait`.
-                                && !matches!(ret_ty.kind(), ty::Opaque(..))
-                        }
-                        _ => false,
-                    };
-                    if !can_coerce_to_return_ty {
-                        return;
-                    }
-
-                    let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
-                    let mut ret_span: MultiSpan = semi_span.into();
-                    ret_span.push_span_label(
-                        expr.span,
-                        "this could be implicitly returned but it is a statement, not a \
-                            tail expression",
-                    );
-                    ret_span
-                        .push_span_label(ret, "the `match` arms can conform to this return type");
-                    ret_span.push_span_label(
-                        semi_span,
-                        "the `match` is a statement because of this semicolon, consider \
-                            removing it",
-                    );
-                    err.span_note(
-                        ret_span,
-                        "you might have meant to return the `match` expression",
-                    );
-                    err.tool_only_span_suggestion(
-                        semi_span,
-                        "remove this semicolon",
-                        "",
-                        Applicability::MaybeIncorrect,
-                    );
+                    self.suggest_removing_semicolon_for_coerce(
+                        err,
+                        expr,
+                        orig_expected,
+                        arm_ty,
+                        prior_arm,
+                    )
                 }),
                 false,
             );
@@ -219,6 +177,71 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         coercion.complete(self)
     }
 
+    fn suggest_removing_semicolon_for_coerce(
+        &self,
+        diag: &mut Diagnostic,
+        expr: &hir::Expr<'tcx>,
+        expectation: Expectation<'tcx>,
+        arm_ty: Ty<'tcx>,
+        prior_arm: Option<(Option<hir::HirId>, Ty<'tcx>, Span)>,
+    ) {
+        let hir = self.tcx.hir();
+
+        // First, check that we're actually in the tail of a function.
+        let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Block(block, _), .. }) =
+            hir.get(self.body_id) else { return; };
+        let Some(hir::Stmt { kind: hir::StmtKind::Semi(last_expr), .. })
+            = block.innermost_block().stmts.last() else {  return; };
+        if last_expr.hir_id != expr.hir_id {
+            return;
+        }
+
+        // Next, make sure that we have no type expectation.
+        let Some(ret) = hir
+            .find_by_def_id(self.body_id.owner.def_id)
+            .and_then(|owner| owner.fn_decl())
+            .map(|decl| decl.output.span()) else { return; };
+        let Expectation::IsLast(stmt) = expectation else {
+            return;
+        };
+
+        let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
+            Some(ret_coercion) => {
+                let ret_ty = ret_coercion.borrow().expected_ty();
+                let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
+                self.can_coerce(arm_ty, ret_ty)
+                    && prior_arm.map_or(true, |(_, ty, _)| self.can_coerce(ty, ret_ty))
+                    // The match arms need to unify for the case of `impl Trait`.
+                    && !matches!(ret_ty.kind(), ty::Opaque(..))
+            }
+            _ => false,
+        };
+        if !can_coerce_to_return_ty {
+            return;
+        }
+
+        let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
+        let mut ret_span: MultiSpan = semi_span.into();
+        ret_span.push_span_label(
+            expr.span,
+            "this could be implicitly returned but it is a statement, not a \
+                            tail expression",
+        );
+        ret_span.push_span_label(ret, "the `match` arms can conform to this return type");
+        ret_span.push_span_label(
+            semi_span,
+            "the `match` is a statement because of this semicolon, consider \
+                            removing it",
+        );
+        diag.span_note(ret_span, "you might have meant to return the `match` expression");
+        diag.tool_only_span_suggestion(
+            semi_span,
+            "remove this semicolon",
+            "",
+            Applicability::MaybeIncorrect,
+        );
+    }
+
     /// When the previously checked expression (the scrutinee) diverges,
     /// warn the user about the match arms being unreachable.
     fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
diff --git a/compiler/rustc_hir_typeck/src/check.rs b/compiler/rustc_hir_typeck/src/check.rs
index 37af6e79c3e..3c57e33f6f7 100644
--- a/compiler/rustc_hir_typeck/src/check.rs
+++ b/compiler/rustc_hir_typeck/src/check.rs
@@ -100,7 +100,6 @@ pub(super) fn check_fn<'a, 'tcx>(
 
     inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig);
 
-    fcx.in_tail_expr = true;
     if let ty::Dynamic(..) = declared_ret_ty.kind() {
         // FIXME: We need to verify that the return type is `Sized` after the return expression has
         // been evaluated so that we have types available for all the nodes being returned, but that
@@ -119,7 +118,6 @@ pub(super) fn check_fn<'a, 'tcx>(
         fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType);
         fcx.check_return_expr(&body.value, false);
     }
-    fcx.in_tail_expr = false;
 
     // We insert the deferred_generator_interiors entry after visiting the body.
     // This ensures that all nested generators appear before the entry of this generator.
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index e8bf299b037..25306ebf356 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -705,12 +705,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
 
                 // Object safety violations or miscellaneous.
                 Err(err) => {
-                    self.err_ctxt().report_selection_error(
-                        obligation.clone(),
-                        &obligation,
-                        &err,
-                        false,
-                    );
+                    self.err_ctxt().report_selection_error(obligation.clone(), &obligation, &err);
                     // Treat this like an obligation and follow through
                     // with the unsizing - the lack of a coercion should
                     // be silent, as it causes a type mismatch later.
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 682dbab56bc..89b5e5161a9 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -843,7 +843,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             // Point any obligations that were registered due to opaque type
             // inference at the return expression.
-            self.select_obligations_where_possible(false, |errors| {
+            self.select_obligations_where_possible(|errors| {
                 self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty);
             });
         }
@@ -2738,7 +2738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Some((index_ty, element_ty)) => {
                     // two-phase not needed because index_ty is never mutable
                     self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No);
-                    self.select_obligations_where_possible(false, |errors| {
+                    self.select_obligations_where_possible(|errors| {
                         self.point_at_index_if_possible(errors, idx.span)
                     });
                     element_ty
diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs
index 747ecb036b2..5d44092a5f6 100644
--- a/compiler/rustc_hir_typeck/src/fallback.rs
+++ b/compiler/rustc_hir_typeck/src/fallback.rs
@@ -7,16 +7,16 @@ use rustc_data_structures::{
 use rustc_middle::ty::{self, Ty};
 
 impl<'tcx> FnCtxt<'_, 'tcx> {
-    /// Performs type inference fallback, returning true if any fallback
-    /// occurs.
-    pub(super) fn type_inference_fallback(&self) -> bool {
+    /// Performs type inference fallback, setting `FnCtxt::fallback_has_occurred`
+    /// if fallback has occurred.
+    pub(super) fn type_inference_fallback(&self) {
         debug!(
             "type-inference-fallback start obligations: {:#?}",
             self.fulfillment_cx.borrow_mut().pending_obligations()
         );
 
         // All type checking constraints were added, try to fallback unsolved variables.
-        self.select_obligations_where_possible(false, |_| {});
+        self.select_obligations_where_possible(|_| {});
 
         debug!(
             "type-inference-fallback post selection obligations: {:#?}",
@@ -26,18 +26,17 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // Check if we have any unsolved variables. If not, no need for fallback.
         let unsolved_variables = self.unsolved_variables();
         if unsolved_variables.is_empty() {
-            return false;
+            return;
         }
 
         let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables);
 
-        let mut fallback_has_occurred = false;
         // We do fallback in two passes, to try to generate
         // better error messages.
         // The first time, we do *not* replace opaque types.
         for ty in unsolved_variables {
             debug!("unsolved_variable = {:?}", ty);
-            fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback);
+            self.fallback_if_possible(ty, &diverging_fallback);
         }
 
         // We now see if we can make progress. This might cause us to
@@ -63,9 +62,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         // If we had tried to fallback the opaque inference variable to `MyType`,
         // we will generate a confusing type-check error that does not explicitly
         // refer to opaque types.
-        self.select_obligations_where_possible(fallback_has_occurred, |_| {});
-
-        fallback_has_occurred
+        self.select_obligations_where_possible(|_| {});
     }
 
     // Tries to apply a fallback to `ty` if it is an unsolved variable.
@@ -81,12 +78,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     // Fallback becomes very dubious if we have encountered
     // type-checking errors.  In that case, fallback to Error.
     //
-    // The return value indicates whether fallback has occurred.
+    // Sets `FnCtxt::fallback_has_occurred` if fallback is performed
+    // during this call.
     fn fallback_if_possible(
         &self,
         ty: Ty<'tcx>,
         diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>,
-    ) -> bool {
+    ) {
         // Careful: we do NOT shallow-resolve `ty`. We know that `ty`
         // is an unsolved variable, and we determine its fallback
         // based solely on how it was created, not what other type
@@ -111,7 +109,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64,
             _ => match diverging_fallback.get(&ty) {
                 Some(&fallback_ty) => fallback_ty,
-                None => return false,
+                None => return,
             },
         };
         debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback);
@@ -122,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
             .map(|origin| origin.span)
             .unwrap_or(rustc_span::DUMMY_SP);
         self.demand_eqtype(span, ty, fallback);
-        true
+        self.fallback_has_occurred.set(true);
     }
 
     /// The "diverging fallback" system is rather complicated. This is
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
index 7563c543d3f..35323137e2d 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
@@ -106,7 +106,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // possible. This can help substantially when there are
         // indirect dependencies that don't seem worth tracking
         // precisely.
-        self.select_obligations_where_possible(false, mutate_fulfillment_errors);
+        self.select_obligations_where_possible(mutate_fulfillment_errors);
         self.resolve_vars_if_possible(ty)
     }
 
@@ -600,7 +600,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
         let mut generators = self.deferred_generator_interiors.borrow_mut();
         for (body_id, interior, kind) in generators.drain(..) {
-            self.select_obligations_where_possible(false, |_| {});
+            self.select_obligations_where_possible(|_| {});
             crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
         }
     }
@@ -611,25 +611,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         if !errors.is_empty() {
             self.adjust_fulfillment_errors_for_expr_obligation(&mut errors);
-            self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id, false);
+            self.err_ctxt().report_fulfillment_errors(&errors, self.inh.body_id);
         }
     }
 
     /// Select as many obligations as we can at present.
     pub(in super::super) fn select_obligations_where_possible(
         &self,
-        fallback_has_occurred: bool,
         mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>),
     ) {
         let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self);
         if !result.is_empty() {
             mutate_fulfillment_errors(&mut result);
             self.adjust_fulfillment_errors_for_expr_obligation(&mut result);
-            self.err_ctxt().report_fulfillment_errors(
-                &result,
-                self.inh.body_id,
-                fallback_has_occurred,
-            );
+            self.err_ctxt().report_fulfillment_errors(&result, self.inh.body_id);
         }
     }
 
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 4066cca8a4b..a7a60a19bd3 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -345,7 +345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // an "opportunistic" trait resolution of any trait bounds on
             // the call. This helps coercions.
             if check_closures {
-                self.select_obligations_where_possible(false, |_| {})
+                self.select_obligations_where_possible(|_| {})
             }
 
             // Check each argument, to satisfy the input it was provided for
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
index 72388baa261..d5e4b6de581 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs
@@ -68,10 +68,6 @@ pub struct FnCtxt<'a, 'tcx> {
     /// any).
     pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>,
 
-    /// Used exclusively to reduce cost of advanced evaluation used for
-    /// more helpful diagnostics.
-    pub(super) in_tail_expr: bool,
-
     /// First span of a return site that we find. Used in error messages.
     pub(super) ret_coercion_span: Cell<Option<Span>>,
 
@@ -115,6 +111,8 @@ pub struct FnCtxt<'a, 'tcx> {
     pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>,
 
     pub(super) inh: &'a Inherited<'tcx>,
+
+    pub(super) fallback_has_occurred: Cell<bool>,
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -128,7 +126,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             param_env,
             err_count_on_creation: inh.tcx.sess.err_count(),
             ret_coercion: None,
-            in_tail_expr: false,
             ret_coercion_span: Cell::new(None),
             resume_yield_tys: None,
             ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)),
@@ -138,6 +135,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 by_id: Default::default(),
             }),
             inh,
+            fallback_has_occurred: Cell::new(false),
         }
     }
 
@@ -159,7 +157,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ///
     /// [`InferCtxt::err_ctxt`]: infer::InferCtxt::err_ctxt
     pub fn err_ctxt(&'a self) -> TypeErrCtxt<'a, 'tcx> {
-        TypeErrCtxt { infcx: &self.infcx, typeck_results: Some(self.typeck_results.borrow()) }
+        TypeErrCtxt {
+            infcx: &self.infcx,
+            typeck_results: Some(self.typeck_results.borrow()),
+            fallback_has_occurred: self.fallback_has_occurred.get(),
+        }
     }
 
     pub fn errors_reported_since_creation(&self) -> bool {
diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs
index 624443d9594..052fdef2fc5 100644
--- a/compiler/rustc_hir_typeck/src/lib.rs
+++ b/compiler/rustc_hir_typeck/src/lib.rs
@@ -316,12 +316,12 @@ fn typeck_with_fallback<'tcx>(
             fcx
         };
 
-        let fallback_has_occurred = fcx.type_inference_fallback();
+        fcx.type_inference_fallback();
 
         // Even though coercion casts provide type hints, we check casts after fallback for
         // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
         fcx.check_casts();
-        fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
+        fcx.select_obligations_where_possible(|_| {});
 
         // Closure and generator analysis may run after fallback
         // because they don't constrain other type variables.
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 04ecd2757b4..50e2b0f8926 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -23,10 +23,11 @@ use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef};
 use rustc_span::symbol::{kw, sym, Ident};
 use rustc_span::Symbol;
 use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span};
+use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedNote;
 use rustc_trait_selection::traits::error_reporting::on_unimplemented::TypeErrCtxtExt as _;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 use rustc_trait_selection::traits::{
-    FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote,
+    FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
 };
 
 use std::cmp::Ordering;
diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs
index 8598369e884..c3bcbcc993b 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -772,7 +772,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         match (method, trait_did) {
             (Some(ok), _) => {
                 let method = self.register_infer_ok_obligations(ok);
-                self.select_obligations_where_possible(false, |_| {});
+                self.select_obligations_where_possible(|_| {});
                 Ok(method)
             }
             (None, None) => Err(vec![]),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index e4a76fbd451..3dbc9b9f3f9 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -91,6 +91,7 @@ pub mod nice_region_error;
 pub struct TypeErrCtxt<'a, 'tcx> {
     pub infcx: &'a InferCtxt<'tcx>,
     pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
+    pub fallback_has_occurred: bool,
 }
 
 impl TypeErrCtxt<'_, '_> {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index c2eecd9e87a..d5be9983ea7 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -677,9 +677,9 @@ pub struct CombinedSnapshot<'tcx> {
 
 impl<'tcx> InferCtxt<'tcx> {
     /// Creates a `TypeErrCtxt` for emitting various inference errors.
-    /// During typeck, use `FnCtxt::infer_err` instead.
+    /// During typeck, use `FnCtxt::err_ctxt` instead.
     pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> {
-        TypeErrCtxt { infcx: self, typeck_results: None }
+        TypeErrCtxt { infcx: self, typeck_results: None, fallback_has_occurred: false }
     }
 
     /// calls `tcx.try_unify_abstract_consts` after
diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs
index 1394993abad..54bf4d1d6b7 100644
--- a/compiler/rustc_parse_format/src/lib.rs
+++ b/compiler/rustc_parse_format/src/lib.rs
@@ -819,19 +819,19 @@ fn find_skips_from_snippet(
     };
 
     fn find_skips(snippet: &str, is_raw: bool) -> Vec<usize> {
-        let mut s = snippet.char_indices().peekable();
+        let mut s = snippet.char_indices();
         let mut skips = vec![];
         while let Some((pos, c)) = s.next() {
-            match (c, s.peek()) {
+            match (c, s.clone().next()) {
                 // skip whitespace and empty lines ending in '\\'
                 ('\\', Some((next_pos, '\n'))) if !is_raw => {
                     skips.push(pos);
-                    skips.push(*next_pos);
+                    skips.push(next_pos);
                     let _ = s.next();
 
-                    while let Some((pos, c)) = s.peek() {
+                    while let Some((pos, c)) = s.clone().next() {
                         if matches!(c, ' ' | '\n' | '\t') {
-                            skips.push(*pos);
+                            skips.push(pos);
                             let _ = s.next();
                         } else {
                             break;
@@ -839,7 +839,7 @@ fn find_skips_from_snippet(
                     }
                 }
                 ('\\', Some((next_pos, 'n' | 't' | 'r' | '0' | '\\' | '\'' | '\"'))) => {
-                    skips.push(*next_pos);
+                    skips.push(next_pos);
                     let _ = s.next();
                 }
                 ('\\', Some((_, 'x'))) if !is_raw => {
@@ -858,19 +858,30 @@ fn find_skips_from_snippet(
                     }
                     if let Some((next_pos, next_c)) = s.next() {
                         if next_c == '{' {
-                            skips.push(next_pos);
-                            let mut i = 0; // consume up to 6 hexanumeric chars + closing `}`
-                            while let (Some((next_pos, c)), true) = (s.next(), i < 7) {
-                                if c.is_digit(16) {
-                                    skips.push(next_pos);
-                                } else if c == '}' {
-                                    skips.push(next_pos);
-                                    break;
-                                } else {
-                                    break;
-                                }
-                                i += 1;
+                            // consume up to 6 hexanumeric chars
+                            let digits_len =
+                                s.clone().take(6).take_while(|(_, c)| c.is_digit(16)).count();
+
+                            let len_utf8 = s
+                                .as_str()
+                                .get(..digits_len)
+                                .and_then(|digits| u32::from_str_radix(digits, 16).ok())
+                                .and_then(char::from_u32)
+                                .map_or(1, char::len_utf8);
+
+                            // Skip the digits, for chars that encode to more than 1 utf-8 byte
+                            // exclude as many digits as it is greater than 1 byte
+                            //
+                            // So for a 3 byte character, exclude 2 digits
+                            let required_skips =
+                                digits_len.saturating_sub(len_utf8.saturating_sub(1));
+
+                            // skip '{' and '}' also
+                            for pos in (next_pos..).take(required_skips + 2) {
+                                skips.push(pos)
                             }
+
+                            s.nth(digits_len);
                         } else if next_c.is_digit(16) {
                             skips.push(next_pos);
                             // We suggest adding `{` and `}` when appropriate, accept it here as if
diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
index 58210c75a3d..cada28652f9 100644
--- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::{cvs, Cc, LinkerFlavor, Lld, TargetOptions};
+use crate::spec::{cvs, Cc, DebuginfoKind, LinkerFlavor, Lld, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
 
 pub fn opts() -> TargetOptions {
     // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
@@ -36,7 +37,10 @@ pub fn opts() -> TargetOptions {
         eh_frame_header: false,
         no_default_libraries: false,
         has_thread_local: true,
-
+        // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to
+        // output DWO, despite using DWARF, doesn't use ELF..
+        debuginfo_kind: DebuginfoKind::Pdb,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 54281f91205..c7dee4a18ac 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -3,14 +3,17 @@ pub mod suggestions;
 
 use super::{
     FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes,
-    Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective,
-    OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation,
-    SelectionContext, SelectionError, TraitNotObjectSafe,
+    Obligation, ObligationCause, ObligationCauseCode, OutputTypeParameterMismatch, Overflow,
+    PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe,
 };
-
 use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode};
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::{self, InferCtxt, TyCtxtInferExt};
+use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
+use crate::traits::query::normalize::AtExt as _;
+use crate::traits::specialize::to_pretty_impl_header;
+use on_unimplemented::OnUnimplementedNote;
+use on_unimplemented::TypeErrCtxtExt as _;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{
     pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
@@ -40,11 +43,6 @@ use rustc_span::{ExpnKind, Span, DUMMY_SP};
 use std::fmt;
 use std::iter;
 use std::ops::ControlFlow;
-
-use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use crate::traits::query::normalize::AtExt as _;
-use crate::traits::specialize::to_pretty_impl_header;
-use on_unimplemented::TypeErrCtxtExt as _;
 use suggestions::TypeErrCtxtExt as _;
 
 pub use rustc_infer::traits::error_reporting::*;
@@ -101,7 +99,6 @@ pub trait TypeErrCtxtExt<'tcx> {
         &self,
         errors: &[FulfillmentError<'tcx>],
         body_id: Option<hir::BodyId>,
-        fallback_has_occurred: bool,
     ) -> ErrorGuaranteed;
 
     fn report_overflow_error<T>(
@@ -124,7 +121,6 @@ pub trait TypeErrCtxtExt<'tcx> {
         obligation: PredicateObligation<'tcx>,
         root_obligation: &PredicateObligation<'tcx>,
         error: &SelectionError<'tcx>,
-        fallback_has_occurred: bool,
     );
 }
 
@@ -375,7 +371,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         errors: &[FulfillmentError<'tcx>],
         body_id: Option<hir::BodyId>,
-        fallback_has_occurred: bool,
     ) -> ErrorGuaranteed {
         #[derive(Debug)]
         struct ErrorDescriptor<'tcx> {
@@ -452,7 +447,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 
         for (error, suppressed) in iter::zip(errors, is_suppressed) {
             if !suppressed {
-                self.report_fulfillment_error(error, body_id, fallback_has_occurred);
+                self.report_fulfillment_error(error, body_id);
             }
         }
 
@@ -534,7 +529,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         mut obligation: PredicateObligation<'tcx>,
         root_obligation: &PredicateObligation<'tcx>,
         error: &SelectionError<'tcx>,
-        fallback_has_occurred: bool,
     ) {
         self.set_tainted_by_errors();
         let tcx = self.tcx;
@@ -1015,7 +1009,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         // variable that used to fallback to `()` now falling back to `!`. Issue a
                         // note informing about the change in behaviour.
                         if trait_predicate.skip_binder().self_ty().is_never()
-                            && fallback_has_occurred
+                            && self.fallback_has_occurred
                         {
                             let predicate = trait_predicate.map_bound(|mut trait_pred| {
                                 trait_pred.trait_ref.substs = self.tcx.mk_substs_trait(
@@ -1381,7 +1375,6 @@ trait InferCtxtPrivExt<'tcx> {
         &self,
         error: &FulfillmentError<'tcx>,
         body_id: Option<hir::BodyId>,
-        fallback_has_occurred: bool,
     );
 
     fn report_projection_error(
@@ -1531,7 +1524,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         &self,
         error: &FulfillmentError<'tcx>,
         body_id: Option<hir::BodyId>,
-        fallback_has_occurred: bool,
     ) {
         match error.code {
             FulfillmentErrorCode::CodeSelectionError(ref selection_error) => {
@@ -1539,7 +1531,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                     error.obligation.clone(),
                     &error.root_obligation,
                     selection_error,
-                    fallback_has_occurred,
                 );
             }
             FulfillmentErrorCode::CodeProjectionError(ref e) => {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 5eef54c6330..82f0440b307 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -1,14 +1,22 @@
-use super::{
-    ObligationCauseCode, OnUnimplementedDirective, OnUnimplementedNote, PredicateObligation,
-};
+use super::{ObligationCauseCode, PredicateObligation};
 use crate::infer::error_reporting::TypeErrCtxt;
+use rustc_ast::{MetaItem, NestedMetaItem};
+use rustc_attr as attr;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{struct_span_err, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::SubstsRef;
-use rustc_middle::ty::{self, GenericParamDefKind};
-use rustc_span::symbol::sym;
+use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
+use rustc_parse_format::{ParseMode, Parser, Piece, Position};
+use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::{Span, DUMMY_SP};
 use std::iter;
 
+use crate::errors::{
+    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
+};
+
 use super::InferCtxtPrivExt;
 
 pub trait TypeErrCtxtExt<'tcx> {
@@ -276,3 +284,383 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
         }
     }
 }
+
+#[derive(Clone, Debug)]
+pub struct OnUnimplementedFormatString(Symbol);
+
+#[derive(Debug)]
+pub struct OnUnimplementedDirective {
+    pub condition: Option<MetaItem>,
+    pub subcommands: Vec<OnUnimplementedDirective>,
+    pub message: Option<OnUnimplementedFormatString>,
+    pub label: Option<OnUnimplementedFormatString>,
+    pub note: Option<OnUnimplementedFormatString>,
+    pub parent_label: Option<OnUnimplementedFormatString>,
+    pub append_const_msg: Option<Option<Symbol>>,
+}
+
+/// For the `#[rustc_on_unimplemented]` attribute
+#[derive(Default)]
+pub struct OnUnimplementedNote {
+    pub message: Option<String>,
+    pub label: Option<String>,
+    pub note: Option<String>,
+    pub parent_label: Option<String>,
+    /// Append a message for `~const Trait` errors. `None` means not requested and
+    /// should fallback to a generic message, `Some(None)` suggests using the default
+    /// appended message, `Some(Some(s))` suggests use the `s` message instead of the
+    /// default one..
+    pub append_const_msg: Option<Option<Symbol>>,
+}
+
+impl<'tcx> OnUnimplementedDirective {
+    fn parse(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        items: &[NestedMetaItem],
+        span: Span,
+        is_root: bool,
+    ) -> Result<Self, ErrorGuaranteed> {
+        let mut errored = None;
+        let mut item_iter = items.iter();
+
+        let parse_value = |value_str| {
+            OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
+        };
+
+        let condition = if is_root {
+            None
+        } else {
+            let cond = item_iter
+                .next()
+                .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))?
+                .meta_item()
+                .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
+            attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
+                if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
+                    errored = Some(guar);
+                }
+                true
+            });
+            Some(cond.clone())
+        };
+
+        let mut message = None;
+        let mut label = None;
+        let mut note = None;
+        let mut parent_label = None;
+        let mut subcommands = vec![];
+        let mut append_const_msg = None;
+
+        for item in item_iter {
+            if item.has_name(sym::message) && message.is_none() {
+                if let Some(message_) = item.value_str() {
+                    message = parse_value(message_)?;
+                    continue;
+                }
+            } else if item.has_name(sym::label) && label.is_none() {
+                if let Some(label_) = item.value_str() {
+                    label = parse_value(label_)?;
+                    continue;
+                }
+            } else if item.has_name(sym::note) && note.is_none() {
+                if let Some(note_) = item.value_str() {
+                    note = parse_value(note_)?;
+                    continue;
+                }
+            } else if item.has_name(sym::parent_label) && parent_label.is_none() {
+                if let Some(parent_label_) = item.value_str() {
+                    parent_label = parse_value(parent_label_)?;
+                    continue;
+                }
+            } else if item.has_name(sym::on)
+                && is_root
+                && message.is_none()
+                && label.is_none()
+                && note.is_none()
+            {
+                if let Some(items) = item.meta_item_list() {
+                    match Self::parse(tcx, item_def_id, &items, item.span(), false) {
+                        Ok(subcommand) => subcommands.push(subcommand),
+                        Err(reported) => errored = Some(reported),
+                    };
+                    continue;
+                }
+            } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
+                if let Some(msg) = item.value_str() {
+                    append_const_msg = Some(Some(msg));
+                    continue;
+                } else if item.is_word() {
+                    append_const_msg = Some(None);
+                    continue;
+                }
+            }
+
+            // nothing found
+            tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
+        }
+
+        if let Some(reported) = errored {
+            Err(reported)
+        } else {
+            Ok(OnUnimplementedDirective {
+                condition,
+                subcommands,
+                message,
+                label,
+                note,
+                parent_label,
+                append_const_msg,
+            })
+        }
+    }
+
+    pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
+        let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
+            return Ok(None);
+        };
+
+        let result = if let Some(items) = attr.meta_item_list() {
+            Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
+        } else if let Some(value) = attr.value_str() {
+            Ok(Some(OnUnimplementedDirective {
+                condition: None,
+                message: None,
+                subcommands: vec![],
+                label: Some(OnUnimplementedFormatString::try_parse(
+                    tcx,
+                    item_def_id,
+                    value,
+                    attr.span,
+                )?),
+                note: None,
+                parent_label: None,
+                append_const_msg: None,
+            }))
+        } else {
+            let reported =
+                tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
+            return Err(reported);
+        };
+        debug!("of_item({:?}) = {:?}", item_def_id, result);
+        result
+    }
+
+    pub fn evaluate(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        options: &[(Symbol, Option<String>)],
+    ) -> OnUnimplementedNote {
+        let mut message = None;
+        let mut label = None;
+        let mut note = None;
+        let mut parent_label = None;
+        let mut append_const_msg = None;
+        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
+
+        let options_map: FxHashMap<Symbol, String> =
+            options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
+
+        for command in self.subcommands.iter().chain(Some(self)).rev() {
+            if let Some(ref condition) = command.condition && !attr::eval_condition(
+                condition,
+                &tcx.sess.parse_sess,
+                Some(tcx.features()),
+                &mut |cfg| {
+                    let value = cfg.value.map(|v| {
+                        OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
+                    });
+
+                    options.contains(&(cfg.name, value))
+                },
+            ) {
+                debug!("evaluate: skipping {:?} due to condition", command);
+                continue;
+            }
+            debug!("evaluate: {:?} succeeded", command);
+            if let Some(ref message_) = command.message {
+                message = Some(message_.clone());
+            }
+
+            if let Some(ref label_) = command.label {
+                label = Some(label_.clone());
+            }
+
+            if let Some(ref note_) = command.note {
+                note = Some(note_.clone());
+            }
+
+            if let Some(ref parent_label_) = command.parent_label {
+                parent_label = Some(parent_label_.clone());
+            }
+
+            append_const_msg = command.append_const_msg;
+        }
+
+        OnUnimplementedNote {
+            label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
+            message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
+            note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
+            parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
+            append_const_msg,
+        }
+    }
+}
+
+impl<'tcx> OnUnimplementedFormatString {
+    fn try_parse(
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        from: Symbol,
+        err_sp: Span,
+    ) -> Result<Self, ErrorGuaranteed> {
+        let result = OnUnimplementedFormatString(from);
+        result.verify(tcx, item_def_id, err_sp)?;
+        Ok(result)
+    }
+
+    fn verify(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        item_def_id: DefId,
+        span: Span,
+    ) -> Result<(), ErrorGuaranteed> {
+        let trait_def_id = if tcx.is_trait(item_def_id) {
+            item_def_id
+        } else {
+            tcx.trait_id_of_impl(item_def_id)
+                .expect("expected `on_unimplemented` to correspond to a trait")
+        };
+        let trait_name = tcx.item_name(trait_def_id);
+        let generics = tcx.generics_of(item_def_id);
+        let s = self.0.as_str();
+        let parser = Parser::new(s, None, None, false, ParseMode::Format);
+        let mut result = Ok(());
+        for token in parser {
+            match token {
+                Piece::String(_) => (), // Normal string, no need to check it
+                Piece::NextArgument(a) => match a.position {
+                    Position::ArgumentNamed(s) => {
+                        match Symbol::intern(s) {
+                            // `{Self}` is allowed
+                            kw::SelfUpper => (),
+                            // `{ThisTraitsName}` is allowed
+                            s if s == trait_name => (),
+                            // `{from_method}` is allowed
+                            sym::from_method => (),
+                            // `{from_desugaring}` is allowed
+                            sym::from_desugaring => (),
+                            // `{ItemContext}` is allowed
+                            sym::ItemContext => (),
+                            // `{integral}` and `{integer}` and `{float}` are allowed
+                            sym::integral | sym::integer_ | sym::float => (),
+                            // So is `{A}` if A is a type parameter
+                            s => match generics.params.iter().find(|param| param.name == s) {
+                                Some(_) => (),
+                                None => {
+                                    let reported = struct_span_err!(
+                                        tcx.sess,
+                                        span,
+                                        E0230,
+                                        "there is no parameter `{}` on {}",
+                                        s,
+                                        if trait_def_id == item_def_id {
+                                            format!("trait `{}`", trait_name)
+                                        } else {
+                                            "impl".to_string()
+                                        }
+                                    )
+                                    .emit();
+                                    result = Err(reported);
+                                }
+                            },
+                        }
+                    }
+                    // `{:1}` and `{}` are not to be used
+                    Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
+                        let reported = struct_span_err!(
+                            tcx.sess,
+                            span,
+                            E0231,
+                            "only named substitution parameters are allowed"
+                        )
+                        .emit();
+                        result = Err(reported);
+                    }
+                },
+            }
+        }
+
+        result
+    }
+
+    pub fn format(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        trait_ref: ty::TraitRef<'tcx>,
+        options: &FxHashMap<Symbol, String>,
+    ) -> String {
+        let name = tcx.item_name(trait_ref.def_id);
+        let trait_str = tcx.def_path_str(trait_ref.def_id);
+        let generics = tcx.generics_of(trait_ref.def_id);
+        let generic_map = generics
+            .params
+            .iter()
+            .filter_map(|param| {
+                let value = match param.kind {
+                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
+                        trait_ref.substs[param.index as usize].to_string()
+                    }
+                    GenericParamDefKind::Lifetime => return None,
+                };
+                let name = param.name;
+                Some((name, value))
+            })
+            .collect::<FxHashMap<Symbol, String>>();
+        let empty_string = String::new();
+
+        let s = self.0.as_str();
+        let parser = Parser::new(s, None, None, false, ParseMode::Format);
+        let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
+        parser
+            .map(|p| match p {
+                Piece::String(s) => s,
+                Piece::NextArgument(a) => match a.position {
+                    Position::ArgumentNamed(s) => {
+                        let s = Symbol::intern(s);
+                        match generic_map.get(&s) {
+                            Some(val) => val,
+                            None if s == name => &trait_str,
+                            None => {
+                                if let Some(val) = options.get(&s) {
+                                    val
+                                } else if s == sym::from_desugaring || s == sym::from_method {
+                                    // don't break messages using these two arguments incorrectly
+                                    &empty_string
+                                } else if s == sym::ItemContext {
+                                    &item_context
+                                } else if s == sym::integral {
+                                    "{integral}"
+                                } else if s == sym::integer_ {
+                                    "{integer}"
+                                } else if s == sym::float {
+                                    "{float}"
+                                } else {
+                                    bug!(
+                                        "broken on_unimplemented {:?} for {:?}: \
+                                      no argument matching {:?}",
+                                        self.0,
+                                        trait_ref,
+                                        s
+                                    )
+                                }
+                            }
+                        }
+                    }
+                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
+                },
+            })
+            .collect()
+    }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index be603c609cb..b6ded4ce5a3 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -70,7 +70,7 @@ pub fn can_type_implement_copy<'tcx>(
                     }
                 }
                 Err(errors) => {
-                    infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+                    infcx.err_ctxt().report_fulfillment_errors(&errors, None);
                 }
             };
         }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index a33534f5747..10e48610e3a 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -12,7 +12,6 @@ pub mod error_reporting;
 mod fulfill;
 pub mod misc;
 mod object_safety;
-mod on_unimplemented;
 pub mod outlives_bounds;
 mod project;
 pub mod query;
@@ -58,7 +57,6 @@ pub use self::object_safety::astconv_object_safety_violations;
 pub use self::object_safety::is_vtable_safe_method;
 pub use self::object_safety::MethodViolationCode;
 pub use self::object_safety::ObjectSafetyViolation;
-pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote};
 pub use self::project::{normalize, normalize_projection_type, normalize_to};
 pub use self::select::{EvaluationCache, SelectionCache, SelectionContext};
 pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError};
@@ -214,7 +212,7 @@ fn do_normalize_predicates<'tcx>(
     let predicates = match fully_normalize(&infcx, cause, elaborated_env, predicates) {
         Ok(predicates) => predicates,
         Err(errors) => {
-            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
             return Err(reported);
         }
     };
diff --git a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
deleted file mode 100644
index fb062ea71c4..00000000000
--- a/compiler/rustc_trait_selection/src/traits/on_unimplemented.rs
+++ /dev/null
@@ -1,393 +0,0 @@
-use rustc_ast::{MetaItem, NestedMetaItem};
-use rustc_attr as attr;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, ErrorGuaranteed};
-use rustc_hir::def_id::DefId;
-use rustc_middle::ty::{self, GenericParamDefKind, TyCtxt};
-use rustc_parse_format::{ParseMode, Parser, Piece, Position};
-use rustc_span::symbol::{kw, sym, Symbol};
-use rustc_span::{Span, DUMMY_SP};
-
-use crate::errors::{
-    EmptyOnClauseInOnUnimplemented, InvalidOnClauseInOnUnimplemented, NoValueInOnUnimplemented,
-};
-
-#[derive(Clone, Debug)]
-pub struct OnUnimplementedFormatString(Symbol);
-
-#[derive(Debug)]
-pub struct OnUnimplementedDirective {
-    pub condition: Option<MetaItem>,
-    pub subcommands: Vec<OnUnimplementedDirective>,
-    pub message: Option<OnUnimplementedFormatString>,
-    pub label: Option<OnUnimplementedFormatString>,
-    pub note: Option<OnUnimplementedFormatString>,
-    pub parent_label: Option<OnUnimplementedFormatString>,
-    pub append_const_msg: Option<Option<Symbol>>,
-}
-
-#[derive(Default)]
-/// For the `#[rustc_on_unimplemented]` attribute
-pub struct OnUnimplementedNote {
-    pub message: Option<String>,
-    pub label: Option<String>,
-    pub note: Option<String>,
-    pub parent_label: Option<String>,
-    /// Append a message for `~const Trait` errors. `None` means not requested and
-    /// should fallback to a generic message, `Some(None)` suggests using the default
-    /// appended message, `Some(Some(s))` suggests use the `s` message instead of the
-    /// default one..
-    pub append_const_msg: Option<Option<Symbol>>,
-}
-
-impl<'tcx> OnUnimplementedDirective {
-    fn parse(
-        tcx: TyCtxt<'tcx>,
-        item_def_id: DefId,
-        items: &[NestedMetaItem],
-        span: Span,
-        is_root: bool,
-    ) -> Result<Self, ErrorGuaranteed> {
-        let mut errored = None;
-        let mut item_iter = items.iter();
-
-        let parse_value = |value_str| {
-            OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span).map(Some)
-        };
-
-        let condition = if is_root {
-            None
-        } else {
-            let cond = item_iter
-                .next()
-                .ok_or_else(|| tcx.sess.emit_err(EmptyOnClauseInOnUnimplemented { span }))?
-                .meta_item()
-                .ok_or_else(|| tcx.sess.emit_err(InvalidOnClauseInOnUnimplemented { span }))?;
-            attr::eval_condition(cond, &tcx.sess.parse_sess, Some(tcx.features()), &mut |cfg| {
-                if let Some(value) = cfg.value && let Err(guar) = parse_value(value) {
-                    errored = Some(guar);
-                }
-                true
-            });
-            Some(cond.clone())
-        };
-
-        let mut message = None;
-        let mut label = None;
-        let mut note = None;
-        let mut parent_label = None;
-        let mut subcommands = vec![];
-        let mut append_const_msg = None;
-
-        for item in item_iter {
-            if item.has_name(sym::message) && message.is_none() {
-                if let Some(message_) = item.value_str() {
-                    message = parse_value(message_)?;
-                    continue;
-                }
-            } else if item.has_name(sym::label) && label.is_none() {
-                if let Some(label_) = item.value_str() {
-                    label = parse_value(label_)?;
-                    continue;
-                }
-            } else if item.has_name(sym::note) && note.is_none() {
-                if let Some(note_) = item.value_str() {
-                    note = parse_value(note_)?;
-                    continue;
-                }
-            } else if item.has_name(sym::parent_label) && parent_label.is_none() {
-                if let Some(parent_label_) = item.value_str() {
-                    parent_label = parse_value(parent_label_)?;
-                    continue;
-                }
-            } else if item.has_name(sym::on)
-                && is_root
-                && message.is_none()
-                && label.is_none()
-                && note.is_none()
-            {
-                if let Some(items) = item.meta_item_list() {
-                    match Self::parse(tcx, item_def_id, &items, item.span(), false) {
-                        Ok(subcommand) => subcommands.push(subcommand),
-                        Err(reported) => errored = Some(reported),
-                    };
-                    continue;
-                }
-            } else if item.has_name(sym::append_const_msg) && append_const_msg.is_none() {
-                if let Some(msg) = item.value_str() {
-                    append_const_msg = Some(Some(msg));
-                    continue;
-                } else if item.is_word() {
-                    append_const_msg = Some(None);
-                    continue;
-                }
-            }
-
-            // nothing found
-            tcx.sess.emit_err(NoValueInOnUnimplemented { span: item.span() });
-        }
-
-        if let Some(reported) = errored {
-            Err(reported)
-        } else {
-            Ok(OnUnimplementedDirective {
-                condition,
-                subcommands,
-                message,
-                label,
-                note,
-                parent_label,
-                append_const_msg,
-            })
-        }
-    }
-
-    pub fn of_item(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<Option<Self>, ErrorGuaranteed> {
-        let Some(attr) = tcx.get_attr(item_def_id, sym::rustc_on_unimplemented) else {
-            return Ok(None);
-        };
-
-        let result = if let Some(items) = attr.meta_item_list() {
-            Self::parse(tcx, item_def_id, &items, attr.span, true).map(Some)
-        } else if let Some(value) = attr.value_str() {
-            Ok(Some(OnUnimplementedDirective {
-                condition: None,
-                message: None,
-                subcommands: vec![],
-                label: Some(OnUnimplementedFormatString::try_parse(
-                    tcx,
-                    item_def_id,
-                    value,
-                    attr.span,
-                )?),
-                note: None,
-                parent_label: None,
-                append_const_msg: None,
-            }))
-        } else {
-            let reported =
-                tcx.sess.delay_span_bug(DUMMY_SP, "of_item: neither meta_item_list nor value_str");
-            return Err(reported);
-        };
-        debug!("of_item({:?}) = {:?}", item_def_id, result);
-        result
-    }
-
-    pub fn evaluate(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
-        options: &[(Symbol, Option<String>)],
-    ) -> OnUnimplementedNote {
-        let mut message = None;
-        let mut label = None;
-        let mut note = None;
-        let mut parent_label = None;
-        let mut append_const_msg = None;
-        info!("evaluate({:?}, trait_ref={:?}, options={:?})", self, trait_ref, options);
-
-        let options_map: FxHashMap<Symbol, String> =
-            options.iter().filter_map(|(k, v)| v.as_ref().map(|v| (*k, v.to_owned()))).collect();
-
-        for command in self.subcommands.iter().chain(Some(self)).rev() {
-            if let Some(ref condition) = command.condition && !attr::eval_condition(
-                condition,
-                &tcx.sess.parse_sess,
-                Some(tcx.features()),
-                &mut |cfg| {
-                    let value = cfg.value.map(|v| {
-                        OnUnimplementedFormatString(v).format(tcx, trait_ref, &options_map)
-                    });
-
-                    options.contains(&(cfg.name, value))
-                },
-            ) {
-                debug!("evaluate: skipping {:?} due to condition", command);
-                continue;
-            }
-            debug!("evaluate: {:?} succeeded", command);
-            if let Some(ref message_) = command.message {
-                message = Some(message_.clone());
-            }
-
-            if let Some(ref label_) = command.label {
-                label = Some(label_.clone());
-            }
-
-            if let Some(ref note_) = command.note {
-                note = Some(note_.clone());
-            }
-
-            if let Some(ref parent_label_) = command.parent_label {
-                parent_label = Some(parent_label_.clone());
-            }
-
-            append_const_msg = command.append_const_msg;
-        }
-
-        OnUnimplementedNote {
-            label: label.map(|l| l.format(tcx, trait_ref, &options_map)),
-            message: message.map(|m| m.format(tcx, trait_ref, &options_map)),
-            note: note.map(|n| n.format(tcx, trait_ref, &options_map)),
-            parent_label: parent_label.map(|e_s| e_s.format(tcx, trait_ref, &options_map)),
-            append_const_msg,
-        }
-    }
-}
-
-impl<'tcx> OnUnimplementedFormatString {
-    fn try_parse(
-        tcx: TyCtxt<'tcx>,
-        item_def_id: DefId,
-        from: Symbol,
-        err_sp: Span,
-    ) -> Result<Self, ErrorGuaranteed> {
-        let result = OnUnimplementedFormatString(from);
-        result.verify(tcx, item_def_id, err_sp)?;
-        Ok(result)
-    }
-
-    fn verify(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        item_def_id: DefId,
-        span: Span,
-    ) -> Result<(), ErrorGuaranteed> {
-        let trait_def_id = if tcx.is_trait(item_def_id) {
-            item_def_id
-        } else {
-            tcx.trait_id_of_impl(item_def_id)
-                .expect("expected `on_unimplemented` to correspond to a trait")
-        };
-        let trait_name = tcx.item_name(trait_def_id);
-        let generics = tcx.generics_of(item_def_id);
-        let s = self.0.as_str();
-        let parser = Parser::new(s, None, None, false, ParseMode::Format);
-        let mut result = Ok(());
-        for token in parser {
-            match token {
-                Piece::String(_) => (), // Normal string, no need to check it
-                Piece::NextArgument(a) => match a.position {
-                    Position::ArgumentNamed(s) => {
-                        match Symbol::intern(s) {
-                            // `{Self}` is allowed
-                            kw::SelfUpper => (),
-                            // `{ThisTraitsName}` is allowed
-                            s if s == trait_name => (),
-                            // `{from_method}` is allowed
-                            sym::from_method => (),
-                            // `{from_desugaring}` is allowed
-                            sym::from_desugaring => (),
-                            // `{ItemContext}` is allowed
-                            sym::ItemContext => (),
-                            // `{integral}` and `{integer}` and `{float}` are allowed
-                            sym::integral | sym::integer_ | sym::float => (),
-                            // So is `{A}` if A is a type parameter
-                            s => match generics.params.iter().find(|param| param.name == s) {
-                                Some(_) => (),
-                                None => {
-                                    let reported = struct_span_err!(
-                                        tcx.sess,
-                                        span,
-                                        E0230,
-                                        "there is no parameter `{}` on {}",
-                                        s,
-                                        if trait_def_id == item_def_id {
-                                            format!("trait `{}`", trait_name)
-                                        } else {
-                                            "impl".to_string()
-                                        }
-                                    )
-                                    .emit();
-                                    result = Err(reported);
-                                }
-                            },
-                        }
-                    }
-                    // `{:1}` and `{}` are not to be used
-                    Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
-                        let reported = struct_span_err!(
-                            tcx.sess,
-                            span,
-                            E0231,
-                            "only named substitution parameters are allowed"
-                        )
-                        .emit();
-                        result = Err(reported);
-                    }
-                },
-            }
-        }
-
-        result
-    }
-
-    pub fn format(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        trait_ref: ty::TraitRef<'tcx>,
-        options: &FxHashMap<Symbol, String>,
-    ) -> String {
-        let name = tcx.item_name(trait_ref.def_id);
-        let trait_str = tcx.def_path_str(trait_ref.def_id);
-        let generics = tcx.generics_of(trait_ref.def_id);
-        let generic_map = generics
-            .params
-            .iter()
-            .filter_map(|param| {
-                let value = match param.kind {
-                    GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => {
-                        trait_ref.substs[param.index as usize].to_string()
-                    }
-                    GenericParamDefKind::Lifetime => return None,
-                };
-                let name = param.name;
-                Some((name, value))
-            })
-            .collect::<FxHashMap<Symbol, String>>();
-        let empty_string = String::new();
-
-        let s = self.0.as_str();
-        let parser = Parser::new(s, None, None, false, ParseMode::Format);
-        let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
-        parser
-            .map(|p| match p {
-                Piece::String(s) => s,
-                Piece::NextArgument(a) => match a.position {
-                    Position::ArgumentNamed(s) => {
-                        let s = Symbol::intern(s);
-                        match generic_map.get(&s) {
-                            Some(val) => val,
-                            None if s == name => &trait_str,
-                            None => {
-                                if let Some(val) = options.get(&s) {
-                                    val
-                                } else if s == sym::from_desugaring || s == sym::from_method {
-                                    // don't break messages using these two arguments incorrectly
-                                    &empty_string
-                                } else if s == sym::ItemContext {
-                                    &item_context
-                                } else if s == sym::integral {
-                                    "{integral}"
-                                } else if s == sym::integer_ {
-                                    "{integer}"
-                                } else if s == sym::float {
-                                    "{float}"
-                                } else {
-                                    bug!(
-                                        "broken on_unimplemented {:?} for {:?}: \
-                                      no argument matching {:?}",
-                                        self.0,
-                                        trait_ref,
-                                        s
-                                    )
-                                }
-                            }
-                        }
-                    }
-                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
-                },
-            })
-            .collect()
-    }
-}
diff --git a/library/core/benches/iter.rs b/library/core/benches/iter.rs
index 38887f29af1..9193c79bee8 100644
--- a/library/core/benches/iter.rs
+++ b/library/core/benches/iter.rs
@@ -1,3 +1,4 @@
+use core::borrow::Borrow;
 use core::iter::*;
 use core::mem;
 use core::num::Wrapping;
@@ -403,13 +404,31 @@ fn bench_trusted_random_access_adapters(b: &mut Bencher) {
 
 /// Exercises the iter::Copied specialization for slice::Iter
 #[bench]
-fn bench_copied_array_chunks(b: &mut Bencher) {
+fn bench_copied_chunks(b: &mut Bencher) {
+    let v = vec![1u8; 1024];
+
+    b.iter(|| {
+        let mut iter = black_box(&v).iter().copied();
+        let mut acc = Wrapping(0);
+        // This uses a while-let loop to side-step the TRA specialization in ArrayChunks
+        while let Ok(chunk) = iter.next_chunk::<{ mem::size_of::<u64>() }>() {
+            let d = u64::from_ne_bytes(chunk);
+            acc += Wrapping(d.rotate_left(7).wrapping_add(1));
+        }
+        acc
+    })
+}
+
+/// Exercises the TrustedRandomAccess specialization in ArrayChunks
+#[bench]
+fn bench_trusted_random_access_chunks(b: &mut Bencher) {
     let v = vec![1u8; 1024];
 
     b.iter(|| {
         black_box(&v)
             .iter()
-            .copied()
+            // this shows that we're not relying on the slice::Iter specialization in Copied
+            .map(|b| *b.borrow())
             .array_chunks::<{ mem::size_of::<u64>() }>()
             .map(|ary| {
                 let d = u64::from_ne_bytes(ary);
diff --git a/library/core/benches/lib.rs b/library/core/benches/lib.rs
index 1e462e3fc3f..cf5a7cada93 100644
--- a/library/core/benches/lib.rs
+++ b/library/core/benches/lib.rs
@@ -5,6 +5,7 @@
 #![feature(test)]
 #![feature(trusted_random_access)]
 #![feature(iter_array_chunks)]
+#![feature(iter_next_chunk)]
 
 extern crate test;
 
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index eae0e1c7618..2090756d7a3 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -865,24 +865,6 @@ where
         return Ok(Try::from_output(unsafe { mem::zeroed() }));
     }
 
-    struct Guard<'a, T, const N: usize> {
-        array_mut: &'a mut [MaybeUninit<T>; N],
-        initialized: usize,
-    }
-
-    impl<T, const N: usize> Drop for Guard<'_, T, N> {
-        fn drop(&mut self) {
-            debug_assert!(self.initialized <= N);
-
-            // SAFETY: this slice will contain only initialized objects.
-            unsafe {
-                crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
-                    &mut self.array_mut.get_unchecked_mut(..self.initialized),
-                ));
-            }
-        }
-    }
-
     let mut array = MaybeUninit::uninit_array::<N>();
     let mut guard = Guard { array_mut: &mut array, initialized: 0 };
 
@@ -896,13 +878,11 @@ where
                     ControlFlow::Continue(elem) => elem,
                 };
 
-                // SAFETY: `guard.initialized` starts at 0, is increased by one in the
-                // loop and the loop is aborted once it reaches N (which is
-                // `array.len()`).
+                // SAFETY: `guard.initialized` starts at 0, which means push can be called
+                // at most N times, which this loop does.
                 unsafe {
-                    guard.array_mut.get_unchecked_mut(guard.initialized).write(item);
+                    guard.push_unchecked(item);
                 }
-                guard.initialized += 1;
             }
             None => {
                 let alive = 0..guard.initialized;
@@ -920,6 +900,55 @@ where
     Ok(Try::from_output(output))
 }
 
+/// Panic guard for incremental initialization of arrays.
+///
+/// Disarm the guard with `mem::forget` once the array has been initialized.
+///
+/// # Safety
+///
+/// All write accesses to this structure are unsafe and must maintain a correct
+/// count of `initialized` elements.
+///
+/// To minimize indirection fields are still pub but callers should at least use
+/// `push_unchecked` to signal that something unsafe is going on.
+pub(crate) struct Guard<'a, T, const N: usize> {
+    /// The array to be initialized.
+    pub array_mut: &'a mut [MaybeUninit<T>; N],
+    /// The number of items that have been initialized so far.
+    pub initialized: usize,
+}
+
+impl<T, const N: usize> Guard<'_, T, N> {
+    /// Adds an item to the array and updates the initialized item counter.
+    ///
+    /// # Safety
+    ///
+    /// No more than N elements must be initialized.
+    #[inline]
+    pub unsafe fn push_unchecked(&mut self, item: T) {
+        // SAFETY: If `initialized` was correct before and the caller does not
+        // invoke this method more than N times then writes will be in-bounds
+        // and slots will not be initialized more than once.
+        unsafe {
+            self.array_mut.get_unchecked_mut(self.initialized).write(item);
+            self.initialized = self.initialized.unchecked_add(1);
+        }
+    }
+}
+
+impl<T, const N: usize> Drop for Guard<'_, T, N> {
+    fn drop(&mut self) {
+        debug_assert!(self.initialized <= N);
+
+        // SAFETY: this slice will contain only initialized objects.
+        unsafe {
+            crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(
+                &mut self.array_mut.get_unchecked_mut(..self.initialized),
+            ));
+        }
+    }
+}
+
 /// Returns the next chunk of `N` items from the iterator or errors with an
 /// iterator over the remainder. Used for `Iterator::next_chunk`.
 #[inline]
diff --git a/library/core/src/iter/adapters/array_chunks.rs b/library/core/src/iter/adapters/array_chunks.rs
index d4fb886101f..5e4211058aa 100644
--- a/library/core/src/iter/adapters/array_chunks.rs
+++ b/library/core/src/iter/adapters/array_chunks.rs
@@ -1,6 +1,8 @@
 use crate::array;
-use crate::iter::{ByRefSized, FusedIterator, Iterator};
-use crate::ops::{ControlFlow, Try};
+use crate::const_closure::ConstFnMutClosure;
+use crate::iter::{ByRefSized, FusedIterator, Iterator, TrustedRandomAccessNoCoerce};
+use crate::mem::{self, MaybeUninit};
+use crate::ops::{ControlFlow, NeverShortCircuit, Try};
 
 /// An iterator over `N` elements of the iterator at a time.
 ///
@@ -82,7 +84,13 @@ where
         }
     }
 
-    impl_fold_via_try_fold! { fold -> try_fold }
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        <Self as SpecFold>::fold(self, init, f)
+    }
 }
 
 #[unstable(feature = "iter_array_chunks", reason = "recently added", issue = "100450")]
@@ -168,3 +176,64 @@ where
         self.iter.len() < N
     }
 }
+
+trait SpecFold: Iterator {
+    fn fold<B, F>(self, init: B, f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B;
+}
+
+impl<I, const N: usize> SpecFold for ArrayChunks<I, N>
+where
+    I: Iterator,
+{
+    #[inline]
+    default fn fold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let fold = ConstFnMutClosure::new(&mut f, NeverShortCircuit::wrap_mut_2_imp);
+        self.try_fold(init, fold).0
+    }
+}
+
+impl<I, const N: usize> SpecFold for ArrayChunks<I, N>
+where
+    I: Iterator + TrustedRandomAccessNoCoerce,
+{
+    #[inline]
+    fn fold<B, F>(mut self, init: B, mut f: F) -> B
+    where
+        Self: Sized,
+        F: FnMut(B, Self::Item) -> B,
+    {
+        let mut accum = init;
+        let inner_len = self.iter.size();
+        let mut i = 0;
+        // Use a while loop because (0..len).step_by(N) doesn't optimize well.
+        while inner_len - i >= N {
+            let mut chunk = MaybeUninit::uninit_array();
+            let mut guard = array::Guard { array_mut: &mut chunk, initialized: 0 };
+            while guard.initialized < N {
+                // SAFETY: The method consumes the iterator and the loop condition ensures that
+                // all accesses are in bounds and only happen once.
+                unsafe {
+                    let idx = i + guard.initialized;
+                    guard.push_unchecked(self.iter.__iterator_get_unchecked(idx));
+                }
+            }
+            mem::forget(guard);
+            // SAFETY: The loop above initialized all elements
+            let chunk = unsafe { MaybeUninit::array_assume_init(chunk) };
+            accum = f(accum, chunk);
+            i += N;
+        }
+
+        // unlike try_fold this method does not need to take care of the remainder
+        // since `self` will be dropped
+
+        accum
+    }
+}
diff --git a/library/core/tests/iter/adapters/array_chunks.rs b/library/core/tests/iter/adapters/array_chunks.rs
index 4e9d89e1e58..ef4a7e53bdd 100644
--- a/library/core/tests/iter/adapters/array_chunks.rs
+++ b/library/core/tests/iter/adapters/array_chunks.rs
@@ -139,7 +139,8 @@ fn test_iterator_array_chunks_fold() {
     let result =
         (0..10).map(|_| CountDrop::new(&count)).array_chunks::<3>().fold(0, |acc, _item| acc + 1);
     assert_eq!(result, 3);
-    assert_eq!(count.get(), 10);
+    // fold impls may or may not process the remainder
+    assert!(count.get() <= 10 && count.get() >= 9);
 }
 
 #[test]
diff --git a/src/doc/rustc/src/linker-plugin-lto.md b/src/doc/rustc/src/linker-plugin-lto.md
index b1854b22a7c..9272b9ac9b2 100644
--- a/src/doc/rustc/src/linker-plugin-lto.md
+++ b/src/doc/rustc/src/linker-plugin-lto.md
@@ -131,24 +131,47 @@ able to get around this problem by setting `-Clinker=lld-link` in RUSTFLAGS
 
 ## Toolchain Compatibility
 
-<!-- NOTE: to update the below table, you can use this shell script:
+<!-- NOTE: to update the below table, you can use this Python script:
 
-```sh
-rustup toolchain install --profile minimal nightly
-MINOR_VERSION=$(rustc +nightly --version | cut -d . -f 2)
-LOWER_BOUND=61
+```python
+from collections import defaultdict
+import subprocess
 
-llvm_version() {
-    toolchain="$1"
-    printf "Rust $toolchain    |    Clang "
-    rustc +"$toolchain" -Vv | grep LLVM | cut -d ':' -f 2 | tr -d ' '
-}
+def minor_version(version):
+    return int(version.split('.')[1])
 
-for version in `seq $LOWER_BOUND $((MINOR_VERSION - 2))`; do
-    toolchain=1.$version.0
-    rustup toolchain install --no-self-update --profile  minimal $toolchain >/dev/null 2>&1
-    llvm_version $toolchain
-done
+INSTALL_TOOLCHAIN = ["rustup", "toolchain", "install", "--profile", "minimal"]
+subprocess.run(INSTALL_TOOLCHAIN + ["nightly"])
+
+LOWER_BOUND = 65
+NIGHTLY_VERSION = minor_version(subprocess.run(
+    ["rustc", "+nightly", "--version"],
+    capture_output=True,
+    text=True).stdout)
+
+def llvm_version(toolchain):
+    version_text = subprocess.run(
+        ["rustc", "+{}".format(toolchain), "-Vv"],
+        capture_output=True,
+        text=True).stdout
+    return int(version_text.split("LLVM")[1].split(':')[1].split('.')[0])
+
+version_map = defaultdict(lambda: [])
+for version in range(LOWER_BOUND, NIGHTLY_VERSION - 1):
+    toolchain = "1.{}.0".format(version)
+    subprocess.run(
+        INSTALL_TOOLCHAIN + ["--no-self-update", toolchain],
+        capture_output=True)
+    version_map[llvm_version(toolchain)].append(version)
+
+print("| Rust Version | Clang Version |")
+print("|--------------|---------------|")
+for clang, rust in sorted(version_map.items()):
+    if len(rust) > 1:
+        rust_range = "1.{} - 1.{}".format(rust[0], rust[-1])
+    else:
+        rust_range = "1.{}       ".format(rust[0])
+    print("| {}  |      {}       |".format(rust_range, clang))
 ```
 
 -->
@@ -166,32 +189,13 @@ The following table shows known good combinations of toolchain versions.
 
 | Rust Version | Clang Version |
 |--------------|---------------|
-| Rust 1.34    |    Clang 8    |
-| Rust 1.35    |    Clang 8    |
-| Rust 1.36    |    Clang 8    |
-| Rust 1.37    |    Clang 8    |
-| Rust 1.38    |    Clang 9    |
-| Rust 1.39    |    Clang 9    |
-| Rust 1.40    |    Clang 9    |
-| Rust 1.41    |    Clang 9    |
-| Rust 1.42    |    Clang 9    |
-| Rust 1.43    |    Clang 9    |
-| Rust 1.44    |    Clang 9    |
-| Rust 1.45    |    Clang 10   |
-| Rust 1.46    |    Clang 10   |
-| Rust 1.47    |    Clang 11   |
-| Rust 1.48    |    Clang 11   |
-| Rust 1.49    |    Clang 11   |
-| Rust 1.50    |    Clang 11   |
-| Rust 1.51    |    Clang 11   |
-| Rust 1.52    |    Clang 12   |
-| Rust 1.53    |    Clang 12   |
-| Rust 1.54    |    Clang 12   |
-| Rust 1.55    |    Clang 12   |
-| Rust 1.56    |    Clang 13   |
-| Rust 1.57    |    Clang 13   |
-| Rust 1.58    |    Clang 13   |
-| Rust 1.59    |    Clang 13   |
-| Rust 1.60    |    Clang 14   |
+| 1.34 - 1.37  |       8       |
+| 1.38 - 1.44  |       9       |
+| 1.45 - 1.46  |      10       |
+| 1.47 - 1.51  |      11       |
+| 1.52 - 1.55  |      12       |
+| 1.56 - 1.59  |      13       |
+| 1.60 - 1.64  |      14       |
+| 1.65         |      15       |
 
 Note that the compatibility policy for this feature might change in the future.
diff --git a/src/test/ui/fmt/unicode-escape-spans.rs b/src/test/ui/fmt/unicode-escape-spans.rs
new file mode 100644
index 00000000000..753d91ce58e
--- /dev/null
+++ b/src/test/ui/fmt/unicode-escape-spans.rs
@@ -0,0 +1,19 @@
+fn main() {
+    // 1 byte in UTF-8
+    format!("\u{000041}{a}"); //~ ERROR cannot find value
+    format!("\u{0041}{a}"); //~ ERROR cannot find value
+    format!("\u{41}{a}"); //~ ERROR cannot find value
+    format!("\u{0}{a}"); //~ ERROR cannot find value
+
+    // 2 bytes
+    format!("\u{0df}{a}"); //~ ERROR cannot find value
+    format!("\u{df}{a}"); //~ ERROR cannot find value
+
+    // 3 bytes
+    format!("\u{00211d}{a}"); //~ ERROR cannot find value
+    format!("\u{211d}{a}"); //~ ERROR cannot find value
+
+    // 4 bytes
+    format!("\u{1f4a3}{a}"); //~ ERROR cannot find value
+    format!("\u{10ffff}{a}"); //~ ERROR cannot find value
+}
diff --git a/src/test/ui/fmt/unicode-escape-spans.stderr b/src/test/ui/fmt/unicode-escape-spans.stderr
new file mode 100644
index 00000000000..1d8473f01b8
--- /dev/null
+++ b/src/test/ui/fmt/unicode-escape-spans.stderr
@@ -0,0 +1,63 @@
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:3:25
+   |
+LL |     format!("\u{000041}{a}");
+   |                         ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:4:23
+   |
+LL |     format!("\u{0041}{a}");
+   |                       ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:5:21
+   |
+LL |     format!("\u{41}{a}");
+   |                     ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:6:20
+   |
+LL |     format!("\u{0}{a}");
+   |                    ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:9:22
+   |
+LL |     format!("\u{0df}{a}");
+   |                      ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:10:21
+   |
+LL |     format!("\u{df}{a}");
+   |                     ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:13:25
+   |
+LL |     format!("\u{00211d}{a}");
+   |                         ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:14:23
+   |
+LL |     format!("\u{211d}{a}");
+   |                       ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:17:24
+   |
+LL |     format!("\u{1f4a3}{a}");
+   |                        ^ not found in this scope
+
+error[E0425]: cannot find value `a` in this scope
+  --> $DIR/unicode-escape-spans.rs:18:25
+   |
+LL |     format!("\u{10ffff}{a}");
+   |                         ^ not found in this scope
+
+error: aborting due to 10 previous errors
+
+For more information about this error, try `rustc --explain E0425`.