about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_borrowck/src/region_infer/opaque_types.rs4
-rw-r--r--compiler/rustc_borrowck/src/type_check/free_region_relations.rs5
-rw-r--r--compiler/rustc_borrowck/src/type_check/mod.rs4
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/check.rs3
-rw-r--r--compiler/rustc_driver/src/lib.rs11
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs26
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs5
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs14
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs4
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/_match.rs123
-rw-r--r--compiler/rustc_hir_typeck/src/check.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs11
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs23
-rw-r--r--compiler/rustc_hir_typeck/src/fallback.rs26
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs18
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/mod.rs14
-rw-r--r--compiler/rustc_hir_typeck/src/lib.rs4
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/op.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/pat.rs6
-rw-r--r--compiler/rustc_hir_typeck/src/place_op.rs7
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs1
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs4
-rw-r--r--compiler/rustc_lint/src/context.rs4
-rw-r--r--compiler/rustc_lint/src/levels.rs1
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/middle/privacy.rs124
-rw-r--r--compiler/rustc_middle/src/ty/context.rs17
-rw-r--r--compiler/rustc_middle/src/ty/visit.rs6
-rw-r--r--compiler/rustc_parse_format/src/lib.rs47
-rw-r--r--compiler/rustc_privacy/src/lib.rs10
-rw-r--r--compiler/rustc_resolve/src/effective_visibilities.rs222
-rw-r--r--compiler/rustc_resolve/src/lib.rs19
-rw-r--r--compiler/rustc_session/src/config.rs2
-rw-r--r--compiler/rustc_target/src/spec/windows_gnullvm_base.rs8
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs27
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs398
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/on_unimplemented.rs393
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/normalize.rs4
-rw-r--r--library/alloc/src/alloc/tests.rs1
-rw-r--r--library/alloc/src/collections/btree/node/tests.rs1
-rw-r--r--library/alloc/src/collections/vec_deque/tests.rs34
-rw-r--r--library/core/benches/iter.rs23
-rw-r--r--library/core/benches/lib.rs1
-rw-r--r--library/core/src/array/mod.rs75
-rw-r--r--library/core/src/iter/adapters/array_chunks.rs75
-rw-r--r--library/core/src/ops/function.rs25
-rw-r--r--library/core/src/slice/mod.rs4
-rw-r--r--library/core/tests/iter/adapters/array_chunks.rs3
-rw-r--r--src/bootstrap/bin/main.rs2
-rw-r--r--src/bootstrap/config.rs5
-rw-r--r--src/bootstrap/setup.rs2
-rw-r--r--src/doc/rustc/src/linker-plugin-lto.md94
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css2
-rw-r--r--src/test/run-make/valid-print-requests/valid-print-requests.stderr2
-rw-r--r--src/test/ui/consts/fn_trait_refs.rs80
-rw-r--r--src/test/ui/fmt/unicode-escape-spans.rs19
-rw-r--r--src/test/ui/fmt/unicode-escape-spans.stderr63
-rw-r--r--src/test/ui/privacy/effective_visibilities.rs4
-rw-r--r--src/test/ui/privacy/effective_visibilities.stderr46
-rw-r--r--src/test/ui/privacy/effective_visibilities_glob.rs21
-rw-r--r--src/test/ui/privacy/effective_visibilities_glob.stderr26
72 files changed, 1401 insertions, 843 deletions
diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
index 95ea42b584a..dd222485daf 100644
--- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
+++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs
@@ -299,8 +299,8 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         if errors.is_empty() {
             definition_ty
         } else {
-            infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
-            self.tcx.ty_error()
+            let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None);
+            self.tcx.ty_error_with_guaranteed(reported)
         }
     }
 }
diff --git a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
index 02909592637..14cfc3613bf 100644
--- a/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
+++ b/compiler/rustc_borrowck/src/type_check/free_region_relations.rs
@@ -247,12 +247,13 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
                     .and(type_op::normalize::Normalize::new(ty))
                     .fully_perform(self.infcx)
                     .unwrap_or_else(|_| {
-                        self.infcx
+                        let reported = self
+                            .infcx
                             .tcx
                             .sess
                             .delay_span_bug(span, &format!("failed to normalize {:?}", ty));
                         TypeOpOutput {
-                            output: self.infcx.tcx.ty_error(),
+                            output: self.infcx.tcx.ty_error_with_guaranteed(reported),
                             constraints: None,
                             error_info: None,
                         }
diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs
index 50af229baaa..9c1d0bb8b23 100644
--- a/compiler/rustc_borrowck/src/type_check/mod.rs
+++ b/compiler/rustc_borrowck/src/type_check/mod.rs
@@ -233,11 +233,11 @@ pub(crate) fn type_check<'mir, 'tcx>(
             let mut hidden_type = infcx.resolve_vars_if_possible(decl.hidden_type);
             trace!("finalized opaque type {:?} to {:#?}", opaque_type_key, hidden_type.ty.kind());
             if hidden_type.has_non_region_infer() {
-                infcx.tcx.sess.delay_span_bug(
+                let reported = infcx.tcx.sess.delay_span_bug(
                     decl.hidden_type.span,
                     &format!("could not resolve {:#?}", hidden_type.ty.kind()),
                 );
-                hidden_type.ty = infcx.tcx.ty_error();
+                hidden_type.ty = infcx.tcx.ty_error_with_guaranteed(reported);
             }
 
             (opaque_type_key, (hidden_type, decl.origin))
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_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index cf4bcc7c158..423f0cdddfd 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -736,6 +736,17 @@ fn print_crate_info(
             // Any output here interferes with Cargo's parsing of other printed output
             NativeStaticLibs => {}
             LinkArgs => {}
+            SplitDebuginfo => {
+                use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
+
+                for split in &[Off, Packed, Unpacked] {
+                    let stable = sess.target.options.supported_split_debuginfo.contains(split);
+                    let unstable_ok = sess.unstable_options();
+                    if stable || unstable_ok {
+                        println!("{}", split);
+                    }
+                }
+            }
         }
     }
     Compilation::Stop
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index ecf8570e81f..730061fca99 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -482,9 +482,9 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
     #[track_caller]
-    pub fn delay_as_bug(&mut self) {
+    pub fn delay_as_bug(&mut self) -> G {
         self.downgrade_to_delayed_bug();
-        self.emit();
+        self.emit()
     }
 
     forward!(
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 8b1cc50a3a1..2665813478c 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -1201,7 +1201,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     (_, _) => {
                         let got = if let Some(_) = term.ty() { "type" } else { "constant" };
                         let expected = def_kind.descr(assoc_item_def_id);
-                        tcx.sess
+                        let reported = tcx
+                            .sess
                             .struct_span_err(
                                 binding.span,
                                 &format!("expected {expected} bound, found {got}"),
@@ -1212,11 +1213,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             )
                             .emit();
                         term = match def_kind {
-                            hir::def::DefKind::AssocTy => tcx.ty_error().into(),
+                            hir::def::DefKind::AssocTy => {
+                                tcx.ty_error_with_guaranteed(reported).into()
+                            }
                             hir::def::DefKind::AssocConst => tcx
-                                .const_error(
+                                .const_error_with_guaranteed(
                                     tcx.bound_type_of(assoc_item_def_id)
                                         .subst(tcx, projection_ty.skip_binder().substs),
+                                    reported,
                                 )
                                 .into(),
                             _ => unreachable!(),
@@ -1334,8 +1338,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 .map(|&(trait_ref, _, _)| trait_ref.def_id())
                 .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
                 .map(|trait_ref| tcx.def_span(trait_ref));
-            tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
-            return tcx.ty_error();
+            let reported =
+                tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
+            return tcx.ty_error_with_guaranteed(reported);
         }
 
         // Check that there are no gross object safety violations;
@@ -1345,14 +1350,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             let object_safety_violations =
                 astconv_object_safety_violations(tcx, item.trait_ref().def_id());
             if !object_safety_violations.is_empty() {
-                report_object_safety_error(
+                let reported = report_object_safety_error(
                     tcx,
                     span,
                     item.trait_ref().def_id(),
                     &object_safety_violations,
                 )
                 .emit();
-                return tcx.ty_error();
+                return tcx.ty_error_with_guaranteed(reported);
             }
         }
 
@@ -2112,13 +2117,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                 "Type"
             };
 
-            self.report_ambiguous_associated_type(
+            let reported = self.report_ambiguous_associated_type(
                 span,
                 type_name,
                 &path_str,
                 item_segment.ident.name,
             );
-            return tcx.ty_error();
+            return tcx.ty_error_with_guaranteed(reported)
         };
 
         debug!("qpath_to_ty: self_type={:?}", self_ty);
@@ -2560,8 +2565,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     {
                         err.span_note(impl_.self_ty.span, "not a concrete type");
                     }
-                    err.emit();
-                    tcx.ty_error()
+                    tcx.ty_error_with_guaranteed(err.emit())
                 } else {
                     self.normalize_ty(span, ty)
                 }
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..660c56ee8b0 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);
     }
 
@@ -611,11 +611,11 @@ pub fn collect_trait_impl_trait_tys<'tcx>(
                 collected_tys.insert(def_id, ty);
             }
             Err(err) => {
-                tcx.sess.delay_span_bug(
+                let reported = tcx.sess.delay_span_bug(
                     return_span,
                     format!("could not fully resolve: {ty} => {err:?}"),
                 );
-                collected_tys.insert(def_id, tcx.ty_error());
+                collected_tys.insert(def_id, tcx.ty_error_with_guaranteed(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/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index 25faacadf3d..4bca16c3a1c 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -512,8 +512,7 @@ impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
                 }
                 _ => {}
             }
-            err.emit();
-            self.tcx().ty_error()
+            self.tcx().ty_error_with_guaranteed(err.emit())
         }
     }
 
diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index c29a645eb4a..2402495c2e4 100644
--- a/compiler/rustc_hir_analysis/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -698,7 +698,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
     }
 
     let Some(hidden) = locator.found else {
-        tcx.sess.emit_err(UnconstrainedOpaqueType {
+        let reported = tcx.sess.emit_err(UnconstrainedOpaqueType {
             span: tcx.def_span(def_id),
             name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
             what: match tcx.hir().get(scope) {
@@ -708,7 +708,7 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
                 _ => "item",
             },
         });
-        return tcx.ty_error();
+        return tcx.ty_error_with_guaranteed(reported);
     };
 
     // Only check against typeck if we didn't already error
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..4d8ab2c1c7a 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.
@@ -1644,9 +1639,9 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                 if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
                     self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
                 }
-                err.emit_unless(unsized_return);
+                let reported = err.emit_unless(unsized_return);
 
-                self.final_ty = Some(fcx.tcx.ty_error());
+                self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
             }
         }
     }
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 682dbab56bc..43669489e69 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -80,14 +80,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // coercions from ! to `expected`.
         if ty.is_never() {
             if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
-                self.tcx().sess.delay_span_bug(
+                let reported = self.tcx().sess.delay_span_bug(
                     expr.span,
                     "expression with never type wound up being adjusted",
                 );
                 return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
                     target.to_owned()
                 } else {
-                    self.tcx().ty_error()
+                    self.tcx().ty_error_with_guaranteed(reported)
                 };
             }
 
@@ -396,8 +396,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         {
                             err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp));
                         }
-                        err.emit();
-                        oprnd_t = tcx.ty_error();
+                        oprnd_t = tcx.ty_error_with_guaranteed(err.emit());
                     }
                 }
                 hir::UnOp::Not => {
@@ -843,7 +842,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);
             });
         }
@@ -1097,12 +1096,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // If the assignment expression itself is ill-formed, don't
             // bother emitting another error
-            if lhs_ty.references_error() || rhs_ty.references_error() {
-                err.delay_as_bug()
-            } else {
-                err.emit();
-            }
-            return self.tcx.ty_error();
+            let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error());
+            return self.tcx.ty_error_with_guaranteed(reported);
         }
 
         let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace);
@@ -2738,7 +2733,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
@@ -2777,8 +2772,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             );
                         }
                     }
-                    err.emit();
-                    self.tcx.ty_error()
+                    let reported = err.emit();
+                    self.tcx.ty_error_with_guaranteed(reported)
                 }
             }
         }
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..6ed7a93d463 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);
         }
     }
 
@@ -1217,9 +1212,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                             }
                         }
                     }
-                    err.emit();
-
-                    return (tcx.ty_error(), res);
+                    let reported = err.emit();
+                    return (tcx.ty_error_with_guaranteed(reported), res);
                 }
             }
         } else {
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..38b3dd218a9 100644
--- a/compiler/rustc_hir_typeck/src/op.rs
+++ b/compiler/rustc_hir_typeck/src/op.rs
@@ -529,8 +529,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                     }
                 }
-                err.emit();
-                self.tcx.ty_error()
+                let reported = err.emit();
+                self.tcx.ty_error_with_guaranteed(reported)
             }
         };
 
@@ -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_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs
index ea90da4a6dc..c248deb892b 100644
--- a/compiler/rustc_hir_typeck/src/pat.rs
+++ b/compiler/rustc_hir_typeck/src/pat.rs
@@ -1278,12 +1278,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let element_tys = tcx.mk_type_list(element_tys_iter);
         let pat_ty = tcx.mk_ty(ty::Tuple(element_tys));
         if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) {
-            err.emit();
+            let reported = err.emit();
             // Walk subpatterns with an expected type of `err` in this case to silence
             // further errors being emitted when using the bindings. #50333
-            let element_tys_iter = (0..max_len).map(|_| tcx.ty_error());
+            let element_tys_iter = (0..max_len).map(|_| tcx.ty_error_with_guaranteed(reported));
             for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) {
-                self.check_pat(elem, tcx.ty_error(), def_bm, ti);
+                self.check_pat(elem, tcx.ty_error_with_guaranteed(reported), def_bm, ti);
             }
             tcx.mk_tup(element_tys_iter)
         } else {
diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs
index ba8cf6926f3..952ea14887f 100644
--- a/compiler/rustc_hir_typeck/src/place_op.rs
+++ b/compiler/rustc_hir_typeck/src/place_op.rs
@@ -90,8 +90,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Applicability::MachineApplicable,
             );
         }
-        err.emit();
-        Some((self.tcx.ty_error(), self.tcx.ty_error()))
+        let reported = err.emit();
+        Some((
+            self.tcx.ty_error_with_guaranteed(reported),
+            self.tcx.ty_error_with_guaranteed(reported),
+        ))
     }
 
     /// To type-check `base_expr[index_expr]`, we progressively autoderef
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_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index cec0003ffea..28f6ac61c5b 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -579,6 +579,7 @@ pub trait LintContext: Sized {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     fn lookup_with_diagnostics(
         &self,
         lint: &'static Lint,
@@ -882,6 +883,7 @@ pub trait LintContext: Sized {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     fn lookup<S: Into<MultiSpan>>(
         &self,
         lint: &'static Lint,
@@ -908,6 +910,7 @@ pub trait LintContext: Sized {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     fn struct_span_lint<S: Into<MultiSpan>>(
         &self,
         lint: &'static Lint,
@@ -933,6 +936,7 @@ pub trait LintContext: Sized {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     fn lint(
         &self,
         lint: &'static Lint,
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index db0a3419e6a..dfe52312ff0 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -1073,6 +1073,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     pub(crate) fn struct_lint(
         &self,
         lint: &'static Lint,
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 5288fc542d7..ebb7de70e05 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -36,6 +36,7 @@
 #![feature(let_chains)]
 #![feature(min_specialization)]
 #![feature(never_type)]
+#![feature(rustc_attrs)]
 #![recursion_limit = "256"]
 
 #[macro_use]
diff --git a/compiler/rustc_middle/src/middle/privacy.rs b/compiler/rustc_middle/src/middle/privacy.rs
index ffbd6d10da6..3a91522d362 100644
--- a/compiler/rustc_middle/src/middle/privacy.rs
+++ b/compiler/rustc_middle/src/middle/privacy.rs
@@ -1,12 +1,14 @@
 //! A pass that checks to make sure private fields and methods aren't used
 //! outside their scopes. This pass will also generate a set of exported items
 //! which are available for use externally when compiled as a library.
-use crate::ty::{DefIdTree, Visibility};
+use crate::ty::{DefIdTree, TyCtxt, Visibility};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_hir::def::DefKind;
 use rustc_macros::HashStable;
 use rustc_query_system::ich::StableHashingContext;
 use rustc_span::def_id::LocalDefId;
+use std::hash::Hash;
 
 /// Represents the levels of effective visibility an item can have.
 ///
@@ -74,9 +76,9 @@ impl EffectiveVisibility {
 }
 
 /// Holds a map of effective visibilities for reachable HIR nodes.
-#[derive(Default, Clone, Debug)]
-pub struct EffectiveVisibilities {
-    map: FxHashMap<LocalDefId, EffectiveVisibility>,
+#[derive(Clone, Debug)]
+pub struct EffectiveVisibilities<Id = LocalDefId> {
+    map: FxHashMap<Id, EffectiveVisibility>,
 }
 
 impl EffectiveVisibilities {
@@ -111,12 +113,30 @@ impl EffectiveVisibilities {
         })
     }
 
-    pub fn effective_vis(&self, id: LocalDefId) -> Option<&EffectiveVisibility> {
-        self.map.get(&id)
-    }
-
-    pub fn iter(&self) -> impl Iterator<Item = (&LocalDefId, &EffectiveVisibility)> {
-        self.map.iter()
+    // FIXME: Share code with `fn update`.
+    pub fn update_eff_vis(
+        &mut self,
+        def_id: LocalDefId,
+        eff_vis: &EffectiveVisibility,
+        tree: impl DefIdTree,
+    ) {
+        use std::collections::hash_map::Entry;
+        match self.map.entry(def_id) {
+            Entry::Occupied(mut occupied) => {
+                let old_eff_vis = occupied.get_mut();
+                for l in Level::all_levels() {
+                    let vis_at_level = eff_vis.at_level(l);
+                    let old_vis_at_level = old_eff_vis.at_level_mut(l);
+                    if vis_at_level != old_vis_at_level
+                        && vis_at_level.is_at_least(*old_vis_at_level, tree)
+                    {
+                        *old_vis_at_level = *vis_at_level
+                    }
+                }
+                old_eff_vis
+            }
+            Entry::Vacant(vacant) => vacant.insert(*eff_vis),
+        };
     }
 
     pub fn set_public_at_level(
@@ -137,26 +157,82 @@ impl EffectiveVisibilities {
         self.map.insert(id, effective_vis);
     }
 
+    pub fn check_invariants(&self, tcx: TyCtxt<'_>, early: bool) {
+        if !cfg!(debug_assertions) {
+            return;
+        }
+        for (&def_id, ev) in &self.map {
+            // More direct visibility levels can never go farther than less direct ones,
+            // neither of effective visibilities can go farther than nominal visibility,
+            // and all effective visibilities are larger or equal than private visibility.
+            let private_vis = Visibility::Restricted(tcx.parent_module_from_def_id(def_id));
+            let span = tcx.def_span(def_id.to_def_id());
+            if !ev.direct.is_at_least(private_vis, tcx) {
+                span_bug!(span, "private {:?} > direct {:?}", private_vis, ev.direct);
+            }
+            if !ev.reexported.is_at_least(ev.direct, tcx) {
+                span_bug!(span, "direct {:?} > reexported {:?}", ev.direct, ev.reexported);
+            }
+            if !ev.reachable.is_at_least(ev.reexported, tcx) {
+                span_bug!(span, "reexported {:?} > reachable {:?}", ev.reexported, ev.reachable);
+            }
+            if !ev.reachable_through_impl_trait.is_at_least(ev.reachable, tcx) {
+                span_bug!(
+                    span,
+                    "reachable {:?} > reachable_through_impl_trait {:?}",
+                    ev.reachable,
+                    ev.reachable_through_impl_trait
+                );
+            }
+            let nominal_vis = tcx.visibility(def_id);
+            let def_kind = tcx.opt_def_kind(def_id);
+            // FIXME: `rustc_privacy` is not yet updated for the new logic and can set
+            // effective visibilities that are larger than the nominal one.
+            if !nominal_vis.is_at_least(ev.reachable_through_impl_trait, tcx) && early {
+                span_bug!(
+                    span,
+                    "{:?}: reachable_through_impl_trait {:?} > nominal {:?}",
+                    def_id,
+                    ev.reachable_through_impl_trait,
+                    nominal_vis
+                );
+            }
+            // Fully private items are never put into the table, this is important for performance.
+            // FIXME: Fully private `mod` items are currently put into the table.
+            if ev.reachable_through_impl_trait == private_vis && def_kind != Some(DefKind::Mod) {
+                span_bug!(span, "fully private item in the table {:?}: {:?}", def_id, ev.direct);
+            }
+        }
+    }
+}
+
+impl<Id: Eq + Hash> EffectiveVisibilities<Id> {
+    pub fn iter(&self) -> impl Iterator<Item = (&Id, &EffectiveVisibility)> {
+        self.map.iter()
+    }
+
+    pub fn effective_vis(&self, id: Id) -> Option<&EffectiveVisibility> {
+        self.map.get(&id)
+    }
+
     // `parent_id` is not necessarily a parent in source code tree,
     // it is the node from which the maximum effective visibility is inherited.
     pub fn update(
         &mut self,
-        id: LocalDefId,
+        id: Id,
         nominal_vis: Visibility,
-        default_vis: impl FnOnce() -> Visibility,
-        parent_id: LocalDefId,
+        default_vis: Visibility,
+        inherited_eff_vis: Option<EffectiveVisibility>,
         level: Level,
         tree: impl DefIdTree,
     ) -> bool {
         let mut changed = false;
-        let mut current_effective_vis = self.effective_vis(id).copied().unwrap_or_else(|| {
-            if id.is_top_level_module() {
-                EffectiveVisibility::from_vis(Visibility::Public)
-            } else {
-                EffectiveVisibility::from_vis(default_vis())
-            }
-        });
-        if let Some(inherited_effective_vis) = self.effective_vis(parent_id) {
+        let mut current_effective_vis = self
+            .map
+            .get(&id)
+            .copied()
+            .unwrap_or_else(|| EffectiveVisibility::from_vis(default_vis));
+        if let Some(inherited_effective_vis) = inherited_eff_vis {
             let mut inherited_effective_vis_at_prev_level =
                 *inherited_effective_vis.at_level(level);
             let mut calculated_effective_vis = inherited_effective_vis_at_prev_level;
@@ -194,6 +270,12 @@ impl EffectiveVisibilities {
     }
 }
 
+impl<Id> Default for EffectiveVisibilities<Id> {
+    fn default() -> Self {
+        EffectiveVisibilities { map: Default::default() }
+    }
+}
+
 impl<'a> HashStable<StableHashingContext<'a>> for EffectiveVisibilities {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         let EffectiveVisibilities { ref map } = *self;
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index fc706b890d5..2a93df771e1 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -1283,6 +1283,12 @@ impl<'tcx> TyCtxt<'tcx> {
         }
     }
 
+    /// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
+    #[track_caller]
+    pub fn ty_error_with_guaranteed(self, reported: ErrorGuaranteed) -> Ty<'tcx> {
+        self.mk_ty(Error(reported))
+    }
+
     /// Constructs a `TyKind::Error` type and registers a `delay_span_bug` to ensure it gets used.
     #[track_caller]
     pub fn ty_error(self) -> Ty<'tcx> {
@@ -1297,6 +1303,16 @@ impl<'tcx> TyCtxt<'tcx> {
         self.mk_ty(Error(reported))
     }
 
+    /// Like [TyCtxt::ty_error] but for constants, with current `ErrorGuaranteed`
+    #[track_caller]
+    pub fn const_error_with_guaranteed(
+        self,
+        ty: Ty<'tcx>,
+        reported: ErrorGuaranteed,
+    ) -> Const<'tcx> {
+        self.mk_const(ty::ConstKind::Error(reported), ty)
+    }
+
     /// Like [TyCtxt::ty_error] but for constants.
     #[track_caller]
     pub fn const_error(self, ty: Ty<'tcx>) -> Const<'tcx> {
@@ -2856,6 +2872,7 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Return value of the `decorate` closure is ignored, see [`struct_lint_level`] for a detailed explanation.
     ///
     /// [`struct_lint_level`]: rustc_middle::lint::struct_lint_level#decorate-signature
+    #[rustc_lint_diagnostics]
     pub fn struct_lint_node(
         self,
         lint: &'static Lint,
diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs
index f0e9f990a81..5e366ef703f 100644
--- a/compiler/rustc_middle/src/ty/visit.rs
+++ b/compiler/rustc_middle/src/ty/visit.rs
@@ -97,7 +97,11 @@ pub trait TypeVisitable<'tcx>: fmt::Debug + Clone {
     }
     fn error_reported(&self) -> Result<(), ErrorGuaranteed> {
         if self.references_error() {
-            Err(ErrorGuaranteed::unchecked_claim_error_was_emitted())
+            if let Some(reported) = ty::tls::with(|tcx| tcx.sess.has_errors()) {
+                Err(reported)
+            } else {
+                bug!("expect tcx.sess.has_errors return true");
+            }
         } else {
             Ok(())
         }
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_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 865d6306bd3..e17f85c1aae 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -959,6 +959,10 @@ impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
                 for variant in def.variants.iter() {
                     let variant_id = self.tcx.hir().local_def_id(variant.id);
                     self.effective_visibility_diagnostic(variant_id);
+                    if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
+                        let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                        self.effective_visibility_diagnostic(ctor_def_id);
+                    }
                     for field in variant.data.fields() {
                         let def_id = self.tcx.hir().local_def_id(field.hir_id);
                         self.effective_visibility_diagnostic(def_id);
@@ -966,6 +970,10 @@ impl<'tcx, 'a> Visitor<'tcx> for TestReachabilityVisitor<'tcx, 'a> {
                 }
             }
             hir::ItemKind::Struct(ref def, _) | hir::ItemKind::Union(ref def, _) => {
+                if let Some(ctor_hir_id) = def.ctor_hir_id() {
+                    let ctor_def_id = self.tcx.hir().local_def_id(ctor_hir_id);
+                    self.effective_visibility_diagnostic(ctor_def_id);
+                }
                 for field in def.fields() {
                     let def_id = self.tcx.hir().local_def_id(field.hir_id);
                     self.effective_visibility_diagnostic(def_id);
@@ -2131,6 +2139,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
         changed: false,
     };
 
+    visitor.effective_visibilities.check_invariants(tcx, true);
     loop {
         tcx.hir().walk_toplevel_module(&mut visitor);
         if visitor.changed {
@@ -2139,6 +2148,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities {
             break;
         }
     }
+    visitor.effective_visibilities.check_invariants(tcx, false);
 
     let mut check_visitor =
         TestReachabilityVisitor { tcx, effective_visibilities: &visitor.effective_visibilities };
diff --git a/compiler/rustc_resolve/src/effective_visibilities.rs b/compiler/rustc_resolve/src/effective_visibilities.rs
index 17ce854cb43..fa6d34be0cc 100644
--- a/compiler/rustc_resolve/src/effective_visibilities.rs
+++ b/compiler/rustc_resolve/src/effective_visibilities.rs
@@ -1,16 +1,38 @@
-use crate::{ImportKind, NameBindingKind, Resolver};
+use crate::{ImportKind, NameBinding, NameBindingKind, Resolver, ResolverTree};
 use rustc_ast::ast;
 use rustc_ast::visit;
 use rustc_ast::visit::Visitor;
 use rustc_ast::Crate;
 use rustc_ast::EnumDef;
+use rustc_data_structures::intern::Interned;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::def_id::CRATE_DEF_ID;
-use rustc_middle::middle::privacy::Level;
-use rustc_middle::ty::{DefIdTree, Visibility};
+use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level};
+use rustc_middle::ty::Visibility;
+
+type ImportId<'a> = Interned<'a, NameBinding<'a>>;
+
+#[derive(Clone, Copy)]
+enum ParentId<'a> {
+    Def(LocalDefId),
+    Import(ImportId<'a>),
+}
+
+impl ParentId<'_> {
+    fn level(self) -> Level {
+        match self {
+            ParentId::Def(_) => Level::Direct,
+            ParentId::Import(_) => Level::Reexported,
+        }
+    }
+}
 
 pub struct EffectiveVisibilitiesVisitor<'r, 'a> {
     r: &'r mut Resolver<'a>,
+    /// While walking import chains we need to track effective visibilities per-binding, and def id
+    /// keys in `Resolver::effective_visibilities` are not enough for that, because multiple
+    /// bindings can correspond to a single def id in imports. So we keep a separate table.
+    import_effective_visibilities: EffectiveVisibilities<ImportId<'a>>,
     changed: bool,
 }
 
@@ -19,21 +41,57 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
     /// For now, this doesn't resolve macros (FIXME) and cannot resolve Impl, as we
     /// need access to a TyCtxt for that.
     pub fn compute_effective_visibilities<'c>(r: &'r mut Resolver<'a>, krate: &'c Crate) {
-        let mut visitor = EffectiveVisibilitiesVisitor { r, changed: false };
+        let mut visitor = EffectiveVisibilitiesVisitor {
+            r,
+            import_effective_visibilities: Default::default(),
+            changed: false,
+        };
 
-        visitor.update(CRATE_DEF_ID, Visibility::Public, CRATE_DEF_ID, Level::Direct);
+        visitor.update(CRATE_DEF_ID, CRATE_DEF_ID);
         visitor.set_bindings_effective_visibilities(CRATE_DEF_ID);
 
         while visitor.changed {
-            visitor.reset();
+            visitor.changed = false;
             visit::walk_crate(&mut visitor, krate);
         }
 
+        // Update visibilities for import def ids. These are not used during the
+        // `EffectiveVisibilitiesVisitor` pass, because we have more detailed binding-based
+        // information, but are used by later passes. Effective visibility of an import def id
+        // is the maximum value among visibilities of bindings corresponding to that def id.
+        for (binding, eff_vis) in visitor.import_effective_visibilities.iter() {
+            let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
+            if let Some(node_id) = import.id() {
+                let mut update = |node_id| {
+                    r.effective_visibilities.update_eff_vis(
+                        r.local_def_id(node_id),
+                        eff_vis,
+                        ResolverTree(&r.definitions, &r.crate_loader),
+                    )
+                };
+                update(node_id);
+                if let ImportKind::Single { additional_ids: (id1, id2), .. } = import.kind {
+                    // In theory all the single import IDs have individual visibilities and
+                    // effective visibilities, but in practice these IDs go straigth to HIR
+                    // where all their few uses assume that their (effective) visibility
+                    // applies to the whole syntactic `use` item. So they all get the same
+                    // value which is the maximum of all bindings. Maybe HIR for imports
+                    // shouldn't use three IDs at all.
+                    if id1 != ast::DUMMY_NODE_ID {
+                        update(id1);
+                    }
+                    if id2 != ast::DUMMY_NODE_ID {
+                        update(id2);
+                    }
+                }
+            }
+        }
+
         info!("resolve::effective_visibilities: {:#?}", r.effective_visibilities);
     }
 
-    fn reset(&mut self) {
-        self.changed = false;
+    fn nearest_normal_mod(&mut self, def_id: LocalDefId) -> LocalDefId {
+        self.r.get_nearest_non_block_module(def_id.to_def_id()).nearest_parent_mod().expect_local()
     }
 
     /// Update effective visibilities of bindings in the given module,
@@ -48,92 +106,83 @@ impl<'r, 'a> EffectiveVisibilitiesVisitor<'r, 'a> {
                 // Set the given effective visibility level to `Level::Direct` and
                 // sets the rest of the `use` chain to `Level::Reexported` until
                 // we hit the actual exported item.
+                let mut parent_id = ParentId::Def(module_id);
+                while let NameBindingKind::Import { binding: nested_binding, .. } = binding.kind {
+                    let binding_id = ImportId::new_unchecked(binding);
+                    self.update_import(binding_id, parent_id);
 
-                // FIXME: tag and is_public() condition should be removed, but assertions occur.
-                let tag = if binding.is_import() { Level::Reexported } else { Level::Direct };
-                if binding.vis.is_public() {
-                    let mut prev_parent_id = module_id;
-                    let mut level = Level::Direct;
-                    while let NameBindingKind::Import { binding: nested_binding, import, .. } =
-                        binding.kind
-                    {
-                        let mut update = |node_id| {
-                            self.update(
-                                self.r.local_def_id(node_id),
-                                binding.vis.expect_local(),
-                                prev_parent_id,
-                                level,
-                            )
-                        };
-                        match import.kind {
-                            ImportKind::Single { id, additional_ids, .. } => {
-                                // In theory all the import IDs have individual visibilities and
-                                // effective visibilities, but in practice these IDs go straigth to
-                                // HIR where all their few uses assume that their (effective)
-                                // visibility applies to the whole syntactic `use` item. So we
-                                // update them all to the maximum value among the potential
-                                // individual effective visibilities. Maybe HIR for imports
-                                // shouldn't use three IDs at all.
-                                update(id);
-                                update(additional_ids.0);
-                                update(additional_ids.1);
-                                prev_parent_id = self.r.local_def_id(id);
-                            }
-                            ImportKind::Glob { id, .. } | ImportKind::ExternCrate { id, .. } => {
-                                update(id);
-                                prev_parent_id = self.r.local_def_id(id);
-                            }
-                            ImportKind::MacroUse => {
-                                // In theory we should reset the parent id to something private
-                                // here, but `macro_use` imports always refer to external items,
-                                // so it doesn't matter and we can just do nothing.
-                            }
-                            ImportKind::MacroExport => {
-                                // In theory we should reset the parent id to something public
-                                // here, but it has the same effect as leaving the previous parent,
-                                // so we can just do nothing.
-                            }
-                        }
-
-                        level = Level::Reexported;
-                        binding = nested_binding;
-                    }
+                    parent_id = ParentId::Import(binding_id);
+                    binding = nested_binding;
                 }
 
                 if let Some(def_id) = binding.res().opt_def_id().and_then(|id| id.as_local()) {
-                    self.update(def_id, binding.vis.expect_local(), module_id, tag);
+                    self.update_def(def_id, binding.vis.expect_local(), parent_id);
                 }
             }
         }
     }
 
-    fn update(
-        &mut self,
-        def_id: LocalDefId,
+    fn effective_vis(&self, parent_id: ParentId<'a>) -> Option<EffectiveVisibility> {
+        match parent_id {
+            ParentId::Def(def_id) => self.r.effective_visibilities.effective_vis(def_id),
+            ParentId::Import(binding) => self.import_effective_visibilities.effective_vis(binding),
+        }
+        .copied()
+    }
+
+    /// The update is guaranteed to not change the table and we can skip it.
+    fn is_noop_update(
+        &self,
+        parent_id: ParentId<'a>,
         nominal_vis: Visibility,
-        parent_id: LocalDefId,
-        tag: Level,
-    ) {
-        let module_id = self
-            .r
-            .get_nearest_non_block_module(def_id.to_def_id())
-            .nearest_parent_mod()
-            .expect_local();
-        if nominal_vis == Visibility::Restricted(module_id)
-            || self.r.visibilities[&parent_id] == Visibility::Restricted(module_id)
-        {
+        default_vis: Visibility,
+    ) -> bool {
+        nominal_vis == default_vis
+            || match parent_id {
+                ParentId::Def(def_id) => self.r.visibilities[&def_id],
+                ParentId::Import(binding) => binding.vis.expect_local(),
+            } == default_vis
+    }
+
+    fn update_import(&mut self, binding: ImportId<'a>, parent_id: ParentId<'a>) {
+        let NameBindingKind::Import { import, .. } = binding.kind else { unreachable!() };
+        let nominal_vis = binding.vis.expect_local();
+        let default_vis = Visibility::Restricted(
+            import
+                .id()
+                .map(|id| self.nearest_normal_mod(self.r.local_def_id(id)))
+                .unwrap_or(CRATE_DEF_ID),
+        );
+        if self.is_noop_update(parent_id, nominal_vis, default_vis) {
             return;
         }
-        let mut effective_visibilities = std::mem::take(&mut self.r.effective_visibilities);
-        self.changed |= effective_visibilities.update(
+        self.changed |= self.import_effective_visibilities.update(
+            binding,
+            nominal_vis,
+            default_vis,
+            self.effective_vis(parent_id),
+            parent_id.level(),
+            ResolverTree(&self.r.definitions, &self.r.crate_loader),
+        );
+    }
+
+    fn update_def(&mut self, def_id: LocalDefId, nominal_vis: Visibility, parent_id: ParentId<'a>) {
+        let default_vis = Visibility::Restricted(self.nearest_normal_mod(def_id));
+        if self.is_noop_update(parent_id, nominal_vis, default_vis) {
+            return;
+        }
+        self.changed |= self.r.effective_visibilities.update(
             def_id,
             nominal_vis,
-            || Visibility::Restricted(module_id),
-            parent_id,
-            tag,
-            &*self.r,
+            if def_id == CRATE_DEF_ID { Visibility::Public } else { default_vis },
+            self.effective_vis(parent_id),
+            parent_id.level(),
+            ResolverTree(&self.r.definitions, &self.r.crate_loader),
         );
-        self.r.effective_visibilities = effective_visibilities;
+    }
+
+    fn update(&mut self, def_id: LocalDefId, parent_id: LocalDefId) {
+        self.update_def(def_id, self.r.visibilities[&def_id], ParentId::Def(parent_id));
     }
 }
 
@@ -151,12 +200,6 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
                 "ast::ItemKind::MacCall encountered, this should not anymore appear at this stage"
             ),
 
-            // Foreign modules inherit level from parents.
-            ast::ItemKind::ForeignMod(..) => {
-                let parent_id = self.r.local_parent(def_id);
-                self.update(def_id, Visibility::Public, parent_id, Level::Direct);
-            }
-
             ast::ItemKind::Mod(..) => {
                 self.set_bindings_effective_visibilities(def_id);
                 visit::walk_item(self, item);
@@ -167,18 +210,14 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
                 for variant in variants {
                     let variant_def_id = self.r.local_def_id(variant.id);
                     for field in variant.data.fields() {
-                        let field_def_id = self.r.local_def_id(field.id);
-                        let vis = self.r.visibilities[&field_def_id];
-                        self.update(field_def_id, vis, variant_def_id, Level::Direct);
+                        self.update(self.r.local_def_id(field.id), variant_def_id);
                     }
                 }
             }
 
             ast::ItemKind::Struct(ref def, _) | ast::ItemKind::Union(ref def, _) => {
                 for field in def.fields() {
-                    let field_def_id = self.r.local_def_id(field.id);
-                    let vis = self.r.visibilities[&field_def_id];
-                    self.update(field_def_id, vis, def_id, Level::Direct);
+                    self.update(self.r.local_def_id(field.id), def_id);
                 }
             }
 
@@ -194,6 +233,7 @@ impl<'r, 'ast> Visitor<'ast> for EffectiveVisibilitiesVisitor<'ast, 'r> {
             | ast::ItemKind::TyAlias(..)
             | ast::ItemKind::TraitAlias(..)
             | ast::ItemKind::MacroDef(..)
+            | ast::ItemKind::ForeignMod(..)
             | ast::ItemKind::Fn(..) => return,
         }
     }
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index ee1c97d5ad2..a1ff477c6fe 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1106,17 +1106,30 @@ impl<'a> AsMut<Resolver<'a>> for Resolver<'a> {
     }
 }
 
-impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
+/// A minimal subset of resolver that can implemenent `DefIdTree`, sometimes
+/// required to satisfy borrow checker by avoiding borrowing the whole resolver.
+#[derive(Clone, Copy)]
+struct ResolverTree<'a, 'b>(&'a Definitions, &'a CrateLoader<'b>);
+
+impl DefIdTree for ResolverTree<'_, '_> {
     #[inline]
     fn opt_parent(self, id: DefId) -> Option<DefId> {
+        let ResolverTree(definitions, crate_loader) = self;
         match id.as_local() {
-            Some(id) => self.definitions.def_key(id).parent,
-            None => self.cstore().def_key(id).parent,
+            Some(id) => definitions.def_key(id).parent,
+            None => crate_loader.cstore().def_key(id).parent,
         }
         .map(|index| DefId { index, ..id })
     }
 }
 
+impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
+    #[inline]
+    fn opt_parent(self, id: DefId) -> Option<DefId> {
+        ResolverTree(&self.definitions, &self.crate_loader).opt_parent(id)
+    }
+}
+
 impl Resolver<'_> {
     fn opt_local_def_id(&self, node: NodeId) -> Option<LocalDefId> {
         self.node_id_to_def_id.get(&node).copied()
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index aece29ca0cb..be084adb7b7 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -548,6 +548,7 @@ pub enum PrintRequest {
     NativeStaticLibs,
     StackProtectorStrategies,
     LinkArgs,
+    SplitDebuginfo,
 }
 
 pub enum Input {
@@ -1806,6 +1807,7 @@ fn collect_print_requests(
         ("stack-protector-strategies", PrintRequest::StackProtectorStrategies),
         ("target-spec-json", PrintRequest::TargetSpec),
         ("link-args", PrintRequest::LinkArgs),
+        ("split-debuginfo", PrintRequest::SplitDebuginfo),
     ];
 
     prints.extend(matches.opt_strs("print").into_iter().map(|req| {
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/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index daee5dd8f02..7f829e46bbb 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -647,7 +647,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
     #[instrument(skip(self), level = "debug")]
     fn fold_const(&mut self, constant: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let tcx = self.selcx.tcx();
-        if tcx.lazy_normalization() {
+        if tcx.lazy_normalization() || !needs_normalization(&constant, self.param_env.reveal()) {
             constant
         } else {
             let constant = constant.super_fold_with(self);
diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
index 715f5be8e2f..a7932b332c9 100644
--- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs
@@ -353,6 +353,10 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
         &mut self,
         constant: ty::Const<'tcx>,
     ) -> Result<ty::Const<'tcx>, Self::Error> {
+        if !needs_normalization(&constant, self.param_env.reveal()) {
+            return Ok(constant);
+        }
+
         let constant = constant.try_super_fold_with(self)?;
         debug!(?constant, ?self.param_env);
         Ok(crate::traits::project::with_replaced_escaping_bound_vars(
diff --git a/library/alloc/src/alloc/tests.rs b/library/alloc/src/alloc/tests.rs
index b2f0194599b..1a5938fd34c 100644
--- a/library/alloc/src/alloc/tests.rs
+++ b/library/alloc/src/alloc/tests.rs
@@ -22,7 +22,6 @@ fn allocate_zeroed() {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn alloc_owned_small(b: &mut Bencher) {
     b.iter(|| {
         let _: Box<_> = Box::new(10);
diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs
index aadb0dc9c40..64bce0ff8c0 100644
--- a/library/alloc/src/collections/btree/node/tests.rs
+++ b/library/alloc/src/collections/btree/node/tests.rs
@@ -94,6 +94,7 @@ fn test_partial_eq() {
 
 #[test]
 #[cfg(target_arch = "x86_64")]
+#[cfg_attr(miri, ignore)] // We'd like to run Miri with layout randomization
 fn test_sizes() {
     assert_eq!(core::mem::size_of::<LeafNode<(), ()>>(), 16);
     assert_eq!(core::mem::size_of::<LeafNode<i64, i64>>(), 16 + CAPACITY * 2 * 8);
diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs
index 1f2daef213c..6e0f83020f9 100644
--- a/library/alloc/src/collections/vec_deque/tests.rs
+++ b/library/alloc/src/collections/vec_deque/tests.rs
@@ -3,7 +3,6 @@ use core::iter::TrustedLen;
 use super::*;
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_push_back_100(b: &mut test::Bencher) {
     let mut deq = VecDeque::with_capacity(101);
     b.iter(|| {
@@ -16,7 +15,6 @@ fn bench_push_back_100(b: &mut test::Bencher) {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_push_front_100(b: &mut test::Bencher) {
     let mut deq = VecDeque::with_capacity(101);
     b.iter(|| {
@@ -29,12 +27,15 @@ fn bench_push_front_100(b: &mut test::Bencher) {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_pop_back_100(b: &mut test::Bencher) {
-    let mut deq = VecDeque::<i32>::with_capacity(101);
+    let size = 100;
+    let mut deq = VecDeque::<i32>::with_capacity(size + 1);
+    // We'll mess with private state to pretend like `deq` is filled.
+    // Make sure the buffer is initialized so that we don't read uninit memory.
+    unsafe { deq.ptr().write_bytes(0u8, size + 1) };
 
     b.iter(|| {
-        deq.head = 100;
+        deq.head = size;
         deq.tail = 0;
         while !deq.is_empty() {
             test::black_box(deq.pop_back());
@@ -43,9 +44,9 @@ fn bench_pop_back_100(b: &mut test::Bencher) {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_retain_whole_10000(b: &mut test::Bencher) {
-    let v = (1..100000).collect::<VecDeque<u32>>();
+    let size = if cfg!(miri) { 1000 } else { 100000 };
+    let v = (1..size).collect::<VecDeque<u32>>();
 
     b.iter(|| {
         let mut v = v.clone();
@@ -54,9 +55,9 @@ fn bench_retain_whole_10000(b: &mut test::Bencher) {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_retain_odd_10000(b: &mut test::Bencher) {
-    let v = (1..100000).collect::<VecDeque<u32>>();
+    let size = if cfg!(miri) { 1000 } else { 100000 };
+    let v = (1..size).collect::<VecDeque<u32>>();
 
     b.iter(|| {
         let mut v = v.clone();
@@ -65,23 +66,26 @@ fn bench_retain_odd_10000(b: &mut test::Bencher) {
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_retain_half_10000(b: &mut test::Bencher) {
-    let v = (1..100000).collect::<VecDeque<u32>>();
+    let size = if cfg!(miri) { 1000 } else { 100000 };
+    let v = (1..size).collect::<VecDeque<u32>>();
 
     b.iter(|| {
         let mut v = v.clone();
-        v.retain(|x| *x > 50000)
+        v.retain(|x| *x > size / 2)
     })
 }
 
 #[bench]
-#[cfg_attr(miri, ignore)] // isolated Miri does not support benchmarks
 fn bench_pop_front_100(b: &mut test::Bencher) {
-    let mut deq = VecDeque::<i32>::with_capacity(101);
+    let size = 100;
+    let mut deq = VecDeque::<i32>::with_capacity(size + 1);
+    // We'll mess with private state to pretend like `deq` is filled.
+    // Make sure the buffer is initialized so that we don't read uninit memory.
+    unsafe { deq.ptr().write_bytes(0u8, size + 1) };
 
     b.iter(|| {
-        deq.head = 100;
+        deq.head = size;
         deq.tail = 0;
         while !deq.is_empty() {
             test::black_box(deq.pop_front());
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/src/ops/function.rs b/library/core/src/ops/function.rs
index 8d4b0a7ccac..11b43b621c7 100644
--- a/library/core/src/ops/function.rs
+++ b/library/core/src/ops/function.rs
@@ -576,9 +576,10 @@ mod impls {
     use crate::marker::Tuple;
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: Tuple, F: ?Sized> Fn<A> for &F
+    #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+    impl<A: Tuple, F: ?Sized> const Fn<A> for &F
     where
-        F: Fn<A>,
+        F: ~const Fn<A>,
     {
         extern "rust-call" fn call(&self, args: A) -> F::Output {
             (**self).call(args)
@@ -586,9 +587,10 @@ mod impls {
     }
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: Tuple, F: ?Sized> FnMut<A> for &F
+    #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+    impl<A: Tuple, F: ?Sized> const FnMut<A> for &F
     where
-        F: Fn<A>,
+        F: ~const Fn<A>,
     {
         extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
             (**self).call(args)
@@ -596,9 +598,10 @@ mod impls {
     }
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: Tuple, F: ?Sized> FnOnce<A> for &F
+    #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+    impl<A: Tuple, F: ?Sized> const FnOnce<A> for &F
     where
-        F: Fn<A>,
+        F: ~const Fn<A>,
     {
         type Output = F::Output;
 
@@ -608,9 +611,10 @@ mod impls {
     }
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: Tuple, F: ?Sized> FnMut<A> for &mut F
+    #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+    impl<A: Tuple, F: ?Sized> const FnMut<A> for &mut F
     where
-        F: FnMut<A>,
+        F: ~const FnMut<A>,
     {
         extern "rust-call" fn call_mut(&mut self, args: A) -> F::Output {
             (*self).call_mut(args)
@@ -618,9 +622,10 @@ mod impls {
     }
 
     #[stable(feature = "rust1", since = "1.0.0")]
-    impl<A: Tuple, F: ?Sized> FnOnce<A> for &mut F
+    #[rustc_const_unstable(feature = "const_fn_trait_ref_impls", issue = "101803")]
+    impl<A: Tuple, F: ?Sized> const FnOnce<A> for &mut F
     where
-        F: FnMut<A>,
+        F: ~const FnMut<A>,
     {
         type Output = F::Output;
         extern "rust-call" fn call_once(self, args: A) -> F::Output {
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index 4f1bb17344b..0fde931140f 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -3524,8 +3524,8 @@ impl<T> [T] {
         }
     }
 
-    /// Transmute the slice to a slice of another type, ensuring alignment of the types is
-    /// maintained.
+    /// Transmute the mutable slice to a mutable slice of another type, ensuring alignment of the
+    /// types is maintained.
     ///
     /// This method splits the slice into three distinct slices: prefix, correctly aligned middle
     /// slice of a new type, and the suffix slice. The method may make the middle slice the greatest
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/bootstrap/bin/main.rs b/src/bootstrap/bin/main.rs
index 9b4861ccd95..be69f819c64 100644
--- a/src/bootstrap/bin/main.rs
+++ b/src/bootstrap/bin/main.rs
@@ -35,7 +35,7 @@ fn main() {
 
     // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the
     // changelog warning, not the `x.py setup` message.
-    let suggest_setup = !config.config.exists() && !matches!(config.cmd, Subcommand::Setup { .. });
+    let suggest_setup = config.config.is_none() && !matches!(config.cmd, Subcommand::Setup { .. });
     if suggest_setup {
         println!("warning: you have not made a `config.toml`");
         println!(
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 21dc11c4808..ba50ce9ec24 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -80,7 +80,7 @@ pub struct Config {
     pub keep_stage_std: Vec<u32>,
     pub src: PathBuf,
     /// defaults to `config.toml`
-    pub config: PathBuf,
+    pub config: Option<PathBuf>,
     pub jobs: Option<u32>,
     pub cmd: Subcommand,
     pub incremental: bool,
@@ -926,8 +926,10 @@ impl Config {
         // Give a hard error if `--config` or `RUST_BOOTSTRAP_CONFIG` are set to a missing path,
         // but not if `config.toml` hasn't been created.
         let mut toml = if !using_default_path || toml_path.exists() {
+            config.config = Some(toml_path.clone());
             get_toml(&toml_path)
         } else {
+            config.config = None;
             TomlConfig::default()
         };
 
@@ -942,7 +944,6 @@ impl Config {
         }
 
         config.changelog_seen = toml.changelog_seen;
-        config.config = toml_path;
 
         let build = toml.build.unwrap_or_default();
 
diff --git a/src/bootstrap/setup.rs b/src/bootstrap/setup.rs
index eb7da1bda73..04480277fe0 100644
--- a/src/bootstrap/setup.rs
+++ b/src/bootstrap/setup.rs
@@ -82,7 +82,7 @@ impl fmt::Display for Profile {
 }
 
 pub fn setup(config: &Config, profile: Profile) {
-    let path = &config.config;
+    let path = &config.config.clone().unwrap_or(PathBuf::from("config.toml"));
 
     if path.exists() {
         eprintln!(
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:
-
-```sh
-rustup toolchain install --profile minimal nightly
-MINOR_VERSION=$(rustc +nightly --version | cut -d . -f 2)
-LOWER_BOUND=61
-
-llvm_version() {
-    toolchain="$1"
-    printf "Rust $toolchain    |    Clang "
-    rustc +"$toolchain" -Vv | grep LLVM | cut -d ':' -f 2 | tr -d ' '
-}
-
-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
+<!-- NOTE: to update the below table, you can use this Python script:
+
+```python
+from collections import defaultdict
+import subprocess
+
+def minor_version(version):
+    return int(version.split('.')[1])
+
+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/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index a38c0e42ab4..f58d2c60942 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -854,7 +854,7 @@ so that we can apply CSS-filters to change the arrow color in themes */
 	background-size: 20px;
 	background-position: calc(100% - 2px) 56%;
 	/* image is black color, themes should apply a "filter" property to change the color */
-	background-image: url("down-arrow-2d685a4bae708e15.svg");
+	background-image: url("down-arrow-927217e04c7463ac.svg");
 }
 #crate-search > option {
 	font-size: 1rem;
diff --git a/src/test/run-make/valid-print-requests/valid-print-requests.stderr b/src/test/run-make/valid-print-requests/valid-print-requests.stderr
index 85782866d12..5191e467648 100644
--- a/src/test/run-make/valid-print-requests/valid-print-requests.stderr
+++ b/src/test/run-make/valid-print-requests/valid-print-requests.stderr
@@ -1,2 +1,2 @@
-error: unknown print request `uwu`. Valid print requests are: `crate-name`, `file-names`, `sysroot`, `target-libdir`, `cfg`, `calling-conventions`, `target-list`, `target-cpus`, `target-features`, `relocation-models`, `code-models`, `tls-models`, `native-static-libs`, `stack-protector-strategies`, `target-spec-json`, `link-args`
+error: unknown print request `uwu`. Valid print requests are: `crate-name`, `file-names`, `sysroot`, `target-libdir`, `cfg`, `calling-conventions`, `target-list`, `target-cpus`, `target-features`, `relocation-models`, `code-models`, `tls-models`, `native-static-libs`, `stack-protector-strategies`, `target-spec-json`, `link-args`, `split-debuginfo`
 
diff --git a/src/test/ui/consts/fn_trait_refs.rs b/src/test/ui/consts/fn_trait_refs.rs
new file mode 100644
index 00000000000..bc8766c74c6
--- /dev/null
+++ b/src/test/ui/consts/fn_trait_refs.rs
@@ -0,0 +1,80 @@
+// build-pass
+
+#![feature(const_fn_trait_ref_impls)]
+#![feature(fn_traits)]
+#![feature(unboxed_closures)]
+#![feature(const_trait_impl)]
+#![feature(const_mut_refs)]
+#![feature(const_cmp)]
+#![feature(const_refs_to_cell)]
+
+use std::marker::Destruct;
+
+const fn tester_fn<T>(f: T) -> T::Output
+where
+    T: ~const Fn<()> + ~const Destruct,
+{
+    f()
+}
+
+const fn tester_fn_mut<T>(mut f: T) -> T::Output
+where
+    T: ~const FnMut<()> + ~const Destruct,
+{
+    f()
+}
+
+const fn tester_fn_once<T>(f: T) -> T::Output
+where
+    T: ~const FnOnce<()>,
+{
+    f()
+}
+
+const fn test_fn<T>(mut f: T) -> (T::Output, T::Output, T::Output)
+where
+    T: ~const Fn<()> + ~const Destruct,
+{
+    (
+        // impl<A: Tuple, F: ~const Fn + ?Sized> const Fn<A> for &F
+        tester_fn(&f),
+        // impl<A: Tuple, F: ~const Fn + ?Sized> const FnMut<A> for &F
+        tester_fn_mut(&f),
+        // impl<A: Tuple, F: ~const Fn + ?Sized> const FnOnce<A> for &F
+        tester_fn_once(&f),
+    )
+}
+
+const fn test_fn_mut<T>(mut f: T) -> (T::Output, T::Output)
+where
+    T: ~const FnMut<()> + ~const Destruct,
+{
+    (
+        // impl<A: Tuple, F: ~const FnMut + ?Sized> const FnMut<A> for &mut F
+        tester_fn_mut(&mut f),
+        // impl<A: Tuple, F: ~const FnMut + ?Sized> const FnOnce<A> for &mut F
+        tester_fn_once(&mut f),
+    )
+}
+const fn test(i: i32) -> i32 {
+    i + 1
+}
+
+const fn main() {
+    const fn one() -> i32 {
+        1
+    };
+    const fn two() -> i32 {
+        2
+    };
+
+    // FIXME(const_cmp_tuple)
+    let test_one = test_fn(one);
+    assert!(test_one.0 == 1);
+    assert!(test_one.1 == 1);
+    assert!(test_one.2 == 1);
+
+    let test_two = test_fn_mut(two);
+    assert!(test_two.0 == 1);
+    assert!(test_two.1 == 1);
+}
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`.
diff --git a/src/test/ui/privacy/effective_visibilities.rs b/src/test/ui/privacy/effective_visibilities.rs
index c1f9ee8dfdf..4479b0d8f61 100644
--- a/src/test/ui/privacy/effective_visibilities.rs
+++ b/src/test/ui/privacy/effective_visibilities.rs
@@ -6,7 +6,7 @@ mod outer { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub
     pub mod inner1 { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
 
         #[rustc_effective_visibility]
-        extern "C" {} //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+        extern "C" {} //~ ERROR not in the table
 
         #[rustc_effective_visibility]
         pub trait PubTrait { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
@@ -18,6 +18,7 @@ mod outer { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub
 
         #[rustc_effective_visibility]
         struct PrivStruct; //~ ERROR not in the table
+                           //~| ERROR not in the table
 
         #[rustc_effective_visibility]
         pub union PubUnion { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
@@ -31,6 +32,7 @@ mod outer { //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub
         pub enum Enum { //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
             #[rustc_effective_visibility]
             A( //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+               //~| ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
                 #[rustc_effective_visibility]
                 PubUnion,  //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
             ),
diff --git a/src/test/ui/privacy/effective_visibilities.stderr b/src/test/ui/privacy/effective_visibilities.stderr
index 5a8f7db38fc..019aaf8086a 100644
--- a/src/test/ui/privacy/effective_visibilities.stderr
+++ b/src/test/ui/privacy/effective_visibilities.stderr
@@ -10,7 +10,7 @@ error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImpl
 LL |     pub mod inner1 {
    |     ^^^^^^^^^^^^^^
 
-error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+error: not in the table
   --> $DIR/effective_visibilities.rs:9:9
    |
 LL |         extern "C" {}
@@ -28,92 +28,104 @@ error: not in the table
 LL |         struct PrivStruct;
    |         ^^^^^^^^^^^^^^^^^
 
+error: not in the table
+  --> $DIR/effective_visibilities.rs:20:9
+   |
+LL |         struct PrivStruct;
+   |         ^^^^^^^^^^^^^^^^^
+
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:23:9
+  --> $DIR/effective_visibilities.rs:24:9
    |
 LL |         pub union PubUnion {
    |         ^^^^^^^^^^^^^^^^^^
 
 error: not in the table
-  --> $DIR/effective_visibilities.rs:25:13
+  --> $DIR/effective_visibilities.rs:26:13
    |
 LL |             a: u8,
    |             ^^^^^
 
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:27:13
+  --> $DIR/effective_visibilities.rs:28:13
    |
 LL |             pub b: u8,
    |             ^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:31:9
+  --> $DIR/effective_visibilities.rs:32:9
    |
 LL |         pub enum Enum {
    |         ^^^^^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:33:13
+  --> $DIR/effective_visibilities.rs:34:13
+   |
+LL |             A(
+   |             ^
+
+error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+  --> $DIR/effective_visibilities.rs:34:13
    |
 LL |             A(
    |             ^
 
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:35:17
+  --> $DIR/effective_visibilities.rs:37:17
    |
 LL |                 PubUnion,
    |                 ^^^^^^^^
 
 error: not in the table
-  --> $DIR/effective_visibilities.rs:41:5
+  --> $DIR/effective_visibilities.rs:43:5
    |
 LL |     macro_rules! none_macro {
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub(self), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:47:5
+  --> $DIR/effective_visibilities.rs:49:5
    |
 LL |     macro_rules! public_macro {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:52:5
+  --> $DIR/effective_visibilities.rs:54:5
    |
 LL |     pub struct ReachableStruct {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:54:9
+  --> $DIR/effective_visibilities.rs:56:9
    |
 LL |         pub a: u8,
    |         ^^^^^^^^^
 
 error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:59:9
+  --> $DIR/effective_visibilities.rs:61:9
    |
 LL | pub use outer::inner1;
    |         ^^^^^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:65:5
+  --> $DIR/effective_visibilities.rs:67:5
    |
 LL |     pub type HalfPublicImport = u8;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate)
-  --> $DIR/effective_visibilities.rs:68:5
+  --> $DIR/effective_visibilities.rs:70:5
    |
 LL |     pub(crate) const HalfPublicImport: u8 = 0;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:72:9
+  --> $DIR/effective_visibilities.rs:74:9
    |
 LL | pub use half_public_import::HalfPublicImport;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
-  --> $DIR/effective_visibilities.rs:72:9
+  --> $DIR/effective_visibilities.rs:74:9
    |
 LL | pub use half_public_import::HalfPublicImport;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -130,5 +142,5 @@ error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImpl
 LL |             type B;
    |             ^^^^^^
 
-error: aborting due to 22 previous errors
+error: aborting due to 24 previous errors
 
diff --git a/src/test/ui/privacy/effective_visibilities_glob.rs b/src/test/ui/privacy/effective_visibilities_glob.rs
new file mode 100644
index 00000000000..eb9dcd6cd1f
--- /dev/null
+++ b/src/test/ui/privacy/effective_visibilities_glob.rs
@@ -0,0 +1,21 @@
+// Effective visibility tracking for imports is fine-grained, so `S2` is not fully exported
+// even if its parent import (`m::*`) is fully exported as a `use` item.
+
+#![feature(rustc_attrs)]
+
+mod m {
+    #[rustc_effective_visibility]
+    pub struct S1 {} //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+    #[rustc_effective_visibility]
+    pub struct S2 {} //~ ERROR Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate)
+}
+
+mod glob {
+    #[rustc_effective_visibility]
+    pub use crate::m::*; //~ ERROR Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+}
+
+#[rustc_effective_visibility]
+pub use glob::S1; //~ ERROR Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+
+fn main() {}
diff --git a/src/test/ui/privacy/effective_visibilities_glob.stderr b/src/test/ui/privacy/effective_visibilities_glob.stderr
new file mode 100644
index 00000000000..0496cd5df8d
--- /dev/null
+++ b/src/test/ui/privacy/effective_visibilities_glob.stderr
@@ -0,0 +1,26 @@
+error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+  --> $DIR/effective_visibilities_glob.rs:8:5
+   |
+LL |     pub struct S1 {}
+   |     ^^^^^^^^^^^^^
+
+error: Direct: pub(crate), Reexported: pub(crate), Reachable: pub(crate), ReachableThroughImplTrait: pub(crate)
+  --> $DIR/effective_visibilities_glob.rs:10:5
+   |
+LL |     pub struct S2 {}
+   |     ^^^^^^^^^^^^^
+
+error: Direct: pub(crate), Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+  --> $DIR/effective_visibilities_glob.rs:15:13
+   |
+LL |     pub use crate::m::*;
+   |             ^^^^^^^^
+
+error: Direct: pub, Reexported: pub, Reachable: pub, ReachableThroughImplTrait: pub
+  --> $DIR/effective_visibilities_glob.rs:19:9
+   |
+LL | pub use glob::S1;
+   |         ^^^^^^^^
+
+error: aborting due to 4 previous errors
+