about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock4
-rw-r--r--compiler/rustc_codegen_cranelift/src/base.rs7
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs2
-rw-r--r--compiler/rustc_error_messages/locales/en-US/monomorphize.ftl26
-rw-r--r--compiler/rustc_error_messages/locales/en-US/session.ftl16
-rw-r--r--compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl7
-rw-r--r--compiler/rustc_error_messages/src/lib.rs3
-rw-r--r--compiler/rustc_hir/src/errors.rs10
-rw-r--r--compiler/rustc_hir/src/lang_items.rs7
-rw-r--r--compiler/rustc_hir/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/middle/lang_items.rs6
-rw-r--r--compiler/rustc_monomorphize/Cargo.toml4
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs67
-rw-r--r--compiler/rustc_monomorphize/src/errors.rs84
-rw-r--r--compiler/rustc_monomorphize/src/lib.rs3
-rw-r--r--compiler/rustc_monomorphize/src/partitioning/mod.rs13
-rw-r--r--compiler/rustc_monomorphize/src/polymorphize.rs11
-rw-r--r--compiler/rustc_session/src/cgu_reuse_tracker.rs43
-rw-r--r--compiler/rustc_session/src/errors.rs45
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/parse.rs21
-rw-r--r--compiler/rustc_symbol_mangling/Cargo.toml2
-rw-r--r--compiler/rustc_symbol_mangling/src/errors.rs36
-rw-r--r--compiler/rustc_symbol_mangling/src/lib.rs3
-rw-r--r--compiler/rustc_symbol_mangling/src/test.rs22
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs2
-rw-r--r--library/alloc/src/vec/into_iter.rs2
-rw-r--r--library/alloc/tests/vec.rs6
-rw-r--r--src/doc/unstable-book/src/compiler-flags/sanitizer.md36
-rw-r--r--src/librustdoc/clean/mod.rs5
-rw-r--r--src/librustdoc/html/format.rs17
-rw-r--r--src/librustdoc/html/render/mod.rs282
-rw-r--r--src/librustdoc/html/render/print_item.rs22
-rw-r--r--src/test/rustdoc-json/fns/async_return.rs36
-rw-r--r--src/test/rustdoc/primitive-reference.rs37
-rw-r--r--src/test/ui/type_length_limit.stderr4
36 files changed, 672 insertions, 221 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 6a375528347..002d73be7d1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3905,8 +3905,10 @@ name = "rustc_monomorphize"
 version = "0.0.0"
 dependencies = [
  "rustc_data_structures",
+ "rustc_errors",
  "rustc_hir",
  "rustc_index",
+ "rustc_macros",
  "rustc_middle",
  "rustc_session",
  "rustc_span",
@@ -4154,7 +4156,9 @@ dependencies = [
  "punycode",
  "rustc-demangle",
  "rustc_data_structures",
+ "rustc_errors",
  "rustc_hir",
+ "rustc_macros",
  "rustc_middle",
  "rustc_session",
  "rustc_span",
diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs
index 3011813c703..c412e451a03 100644
--- a/compiler/rustc_codegen_cranelift/src/base.rs
+++ b/compiler/rustc_codegen_cranelift/src/base.rs
@@ -925,8 +925,11 @@ pub(crate) fn codegen_panic_inner<'tcx>(
     args: &[Value],
     span: Span,
 ) {
-    let def_id =
-        fx.tcx.lang_items().require(lang_item).unwrap_or_else(|s| fx.tcx.sess.span_fatal(span, &s));
+    let def_id = fx
+        .tcx
+        .lang_items()
+        .require(lang_item)
+        .unwrap_or_else(|e| fx.tcx.sess.span_fatal(span, e.to_string()));
 
     let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
     let symbol_name = fx.tcx.symbol_name(instance).name;
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index 2930d09d71f..68f3b19b715 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1892,7 +1892,7 @@ impl<B: ExtraBackendMethods> OngoingCodegen<B> {
             }
         });
 
-        sess.cgu_reuse_tracker.check_expected_reuse(sess.diagnostic());
+        sess.cgu_reuse_tracker.check_expected_reuse(sess);
 
         sess.abort_if_errors();
 
diff --git a/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl b/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl
new file mode 100644
index 00000000000..42c84fdd2d1
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/monomorphize.ftl
@@ -0,0 +1,26 @@
+monomorphize_recursion_limit =
+    reached the recursion limit while instantiating `{$shrunk}`
+    .note = `{$def_path_str}` defined here
+
+monomorphize_written_to_path = the full type name has been written to '{$path}'
+
+monomorphize_type_length_limit = reached the type-length limit while instantiating `{$shrunk}`
+
+monomorphize_consider_type_length_limit =
+    consider adding a `#![type_length_limit="{$type_length}"]` attribute to your crate
+
+monomorphize_fatal_error = {$error_message}
+
+monomorphize_unknown_partition_strategy = unknown partitioning strategy
+
+monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined
+
+monomorphize_unused_generic_params = item has unused generic parameters
+
+monomorphize_large_assignments =
+    moving {$size} bytes
+    .label = value moved from here
+    .note = The current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`
+
+monomorphize_requires_lang_item =
+    requires `{$lang_item}` lang_item
diff --git a/compiler/rustc_error_messages/locales/en-US/session.ftl b/compiler/rustc_error_messages/locales/en-US/session.ftl
new file mode 100644
index 00000000000..983e5cee823
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/session.ftl
@@ -0,0 +1,16 @@
+session_incorrect_cgu_reuse_type =
+    CGU-reuse for `{$cgu_user_name}` is `{$actual_reuse}` but should be {$at_least ->
+    [one] {"at least "}
+    *[other] {""}
+    }`{$expected_reuse}`
+
+session_cgu_not_recorded =
+    CGU-reuse for `{$cgu_user_name}` is (mangled: `{$cgu_name}`) was not recorded`
+
+session_feature_gate_error = {$explain}
+
+session_feature_diagnostic_for_issue =
+    see issue #{$n} <https://github.com/rust-lang/rust/issues/{$n}> for more information
+
+session_feature_diagnostic_help =
+    add `#![feature({$feature})]` to the crate attributes to enable
diff --git a/compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl b/compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl
new file mode 100644
index 00000000000..55d6fbbf86f
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/symbol_mangling.ftl
@@ -0,0 +1,7 @@
+symbol_mangling_invalid_symbol_name = symbol-name({$mangled_formatted})
+
+symbol_mangling_invalid_trait_item = demangling({$demangling_formatted})
+
+symbol_mangling_alt_invalid_trait_item = demangling-alt({$alt_demangling_formatted})
+
+symbol_mangling_invalid_def_path = def-path({$def_path})
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index d1ac326a72c..42fb2d538b0 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -41,9 +41,11 @@ fluent_messages! {
     const_eval => "../locales/en-US/const_eval.ftl",
     driver => "../locales/en-US/driver.ftl",
     expand => "../locales/en-US/expand.ftl",
+    session => "../locales/en-US/session.ftl",
     interface => "../locales/en-US/interface.ftl",
     infer => "../locales/en-US/infer.ftl",
     lint => "../locales/en-US/lint.ftl",
+    monomorphize => "../locales/en-US/monomorphize.ftl",
     parser => "../locales/en-US/parser.ftl",
     passes => "../locales/en-US/passes.ftl",
     plugin_impl => "../locales/en-US/plugin_impl.ftl",
@@ -52,6 +54,7 @@ fluent_messages! {
     ty_utils => "../locales/en-US/ty_utils.ftl",
     typeck => "../locales/en-US/typeck.ftl",
     mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
+    symbol_mangling => "../locales/en-US/symbol_mangling.ftl",
 }
 
 pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
diff --git a/compiler/rustc_hir/src/errors.rs b/compiler/rustc_hir/src/errors.rs
new file mode 100644
index 00000000000..e593ed1044a
--- /dev/null
+++ b/compiler/rustc_hir/src/errors.rs
@@ -0,0 +1,10 @@
+use crate::LangItem;
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Encodable, Decodable)]
+pub struct LangItemError(pub LangItem);
+
+impl ToString for LangItemError {
+    fn to_string(&self) -> String {
+        format!("requires `{}` lang_item", self.0.name())
+    }
+}
diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs
index c337be12ae4..0c01326d003 100644
--- a/compiler/rustc_hir/src/lang_items.rs
+++ b/compiler/rustc_hir/src/lang_items.rs
@@ -8,6 +8,7 @@
 //! * Functions called by the compiler itself.
 
 use crate::def_id::DefId;
+use crate::errors::LangItemError;
 use crate::{MethodKind, Target};
 
 use rustc_ast as ast;
@@ -115,9 +116,9 @@ macro_rules! language_item_table {
 
             /// Requires that a given `LangItem` was bound and returns the corresponding `DefId`.
             /// If it wasn't bound, e.g. due to a missing `#[lang = "<it.name()>"]`,
-            /// returns an error message as a string.
-            pub fn require(&self, it: LangItem) -> Result<DefId, String> {
-                self.items[it as usize].ok_or_else(|| format!("requires `{}` lang_item", it.name()))
+            /// returns an error encapsulating the `LangItem`.
+            pub fn require(&self, it: LangItem) -> Result<DefId, LangItemError> {
+                self.items[it as usize].ok_or_else(|| LangItemError(it))
             }
 
             /// Returns the [`DefId`]s of all lang items in a group.
diff --git a/compiler/rustc_hir/src/lib.rs b/compiler/rustc_hir/src/lib.rs
index 7097f998beb..092029ef09e 100644
--- a/compiler/rustc_hir/src/lib.rs
+++ b/compiler/rustc_hir/src/lib.rs
@@ -27,6 +27,7 @@ pub mod def;
 pub mod def_path_hash_map;
 pub mod definitions;
 pub mod diagnostic_items;
+pub mod errors;
 pub use rustc_span::def_id;
 mod hir;
 pub mod hir_id;
diff --git a/compiler/rustc_middle/src/middle/lang_items.rs b/compiler/rustc_middle/src/middle/lang_items.rs
index cc9706f2d86..31c20fa14aa 100644
--- a/compiler/rustc_middle/src/middle/lang_items.rs
+++ b/compiler/rustc_middle/src/middle/lang_items.rs
@@ -18,11 +18,11 @@ impl<'tcx> TyCtxt<'tcx> {
     /// Returns the `DefId` for a given `LangItem`.
     /// If not found, fatally aborts compilation.
     pub fn require_lang_item(self, lang_item: LangItem, span: Option<Span>) -> DefId {
-        self.lang_items().require(lang_item).unwrap_or_else(|msg| {
+        self.lang_items().require(lang_item).unwrap_or_else(|err| {
             if let Some(span) = span {
-                self.sess.span_fatal(span, &msg)
+                self.sess.span_fatal(span, err.to_string())
             } else {
-                self.sess.fatal(&msg)
+                self.sess.fatal(err.to_string())
             }
         })
     }
diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml
index 41ba4d4b64a..59ca04ec868 100644
--- a/compiler/rustc_monomorphize/Cargo.toml
+++ b/compiler/rustc_monomorphize/Cargo.toml
@@ -7,11 +7,13 @@ edition = "2021"
 doctest = false
 
 [dependencies]
-smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+smallvec = { version = "1.8.1", features = [ "union", "may_dangle" ] }
 tracing = "0.1"
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_session = { path = "../rustc_session" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 5f5540495e9..170616d4b42 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -207,6 +207,8 @@ use std::iter;
 use std::ops::Range;
 use std::path::PathBuf;
 
+use crate::errors::{LargeAssignmentsLint, RecursionLimit, RequiresLangItem, TypeLengthLimit};
+
 #[derive(PartialEq)]
 pub enum MonoItemCollectionMode {
     Eager,
@@ -604,17 +606,24 @@ fn check_recursion_limit<'tcx>(
     // more than the recursion limit is assumed to be causing an
     // infinite expansion.
     if !recursion_limit.value_within_limit(adjusted_recursion_depth) {
+        let def_span = tcx.def_span(def_id);
+        let def_path_str = tcx.def_path_str(def_id);
         let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
-        let error = format!("reached the recursion limit while instantiating `{}`", shrunk);
-        let mut err = tcx.sess.struct_span_fatal(span, &error);
-        err.span_note(
-            tcx.def_span(def_id),
-            &format!("`{}` defined here", tcx.def_path_str(def_id)),
-        );
-        if let Some(path) = written_to_path {
-            err.note(&format!("the full type name has been written to '{}'", path.display()));
-        }
-        err.emit()
+        let mut path = PathBuf::new();
+        let was_written = if written_to_path.is_some() {
+            path = written_to_path.unwrap();
+            Some(())
+        } else {
+            None
+        };
+        tcx.sess.emit_fatal(RecursionLimit {
+            span,
+            shrunk,
+            def_span,
+            def_path_str,
+            was_written,
+            path,
+        });
     }
 
     recursion_depths.insert(def_id, recursion_depth + 1);
@@ -642,16 +651,15 @@ fn check_type_length_limit<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) {
     // Bail out in these cases to avoid that bad user experience.
     if !tcx.type_length_limit().value_within_limit(type_length) {
         let (shrunk, written_to_path) = shrunk_instance_name(tcx, &instance, 32, 32);
-        let msg = format!("reached the type-length limit while instantiating `{}`", shrunk);
-        let mut diag = tcx.sess.struct_span_fatal(tcx.def_span(instance.def_id()), &msg);
-        if let Some(path) = written_to_path {
-            diag.note(&format!("the full type name has been written to '{}'", path.display()));
-        }
-        diag.help(&format!(
-            "consider adding a `#![type_length_limit=\"{}\"]` attribute to your crate",
-            type_length
-        ));
-        diag.emit()
+        let span = tcx.def_span(instance.def_id());
+        let mut path = PathBuf::new();
+        let was_written = if written_to_path.is_some() {
+            path = written_to_path.unwrap();
+            Some(())
+        } else {
+            None
+        };
+        tcx.sess.emit_fatal(TypeLengthLimit { span, shrunk, was_written, path, type_length });
     }
 }
 
@@ -914,17 +922,16 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
                     // but correct span? This would make the lint at least accept crate-level lint attributes.
                     return;
                 };
-                self.tcx.struct_span_lint_hir(
+                self.tcx.emit_spanned_lint(
                     LARGE_ASSIGNMENTS,
                     lint_root,
                     source_info.span,
-                    |lint| {
-                        let mut err = lint.build(&format!("moving {} bytes", layout.size.bytes()));
-                        err.span_label(source_info.span, "value moved from here");
-                        err.note(&format!(r#"The current maximum size is {}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]`"#, limit.bytes()));
-                        err.emit();
+                    LargeAssignmentsLint {
+                        span: source_info.span,
+                        size: layout.size.bytes(),
+                        limit: limit.bytes(),
                     },
-                );
+                )
             }
         }
     }
@@ -1321,7 +1328,11 @@ impl<'v> RootCollector<'_, 'v> {
 
         let start_def_id = match self.tcx.lang_items().require(LangItem::Start) {
             Ok(s) => s,
-            Err(err) => self.tcx.sess.fatal(&err),
+            Err(lang_item_err) => {
+                self.tcx
+                    .sess
+                    .emit_fatal(RequiresLangItem { lang_item: lang_item_err.0.name().to_string() });
+            }
         };
         let main_ret_ty = self.tcx.fn_sig(main_def_id).output();
 
diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs
new file mode 100644
index 00000000000..77b6cfa1f69
--- /dev/null
+++ b/compiler/rustc_monomorphize/src/errors.rs
@@ -0,0 +1,84 @@
+use std::path::PathBuf;
+
+use rustc_errors::ErrorGuaranteed;
+use rustc_macros::{LintDiagnostic, SessionDiagnostic};
+use rustc_session::SessionDiagnostic;
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::recursion_limit)]
+pub struct RecursionLimit {
+    #[primary_span]
+    pub span: Span,
+    pub shrunk: String,
+    #[note]
+    pub def_span: Span,
+    pub def_path_str: String,
+    #[note(monomorphize::written_to_path)]
+    pub was_written: Option<()>,
+    pub path: PathBuf,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::type_length_limit)]
+#[help(monomorphize::consider_type_length_limit)]
+pub struct TypeLengthLimit {
+    #[primary_span]
+    pub span: Span,
+    pub shrunk: String,
+    #[note(monomorphize::written_to_path)]
+    pub was_written: Option<()>,
+    pub path: PathBuf,
+    pub type_length: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::requires_lang_item)]
+pub struct RequiresLangItem {
+    pub lang_item: String,
+}
+
+pub struct UnusedGenericParams {
+    pub span: Span,
+    pub param_spans: Vec<Span>,
+    pub param_names: Vec<String>,
+}
+
+impl SessionDiagnostic<'_> for UnusedGenericParams {
+    fn into_diagnostic(
+        self,
+        sess: &'_ rustc_session::parse::ParseSess,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(rustc_errors::fluent::monomorphize::unused_generic_params);
+        diag.set_span(self.span);
+        for (span, name) in self.param_spans.into_iter().zip(self.param_names) {
+            // FIXME: I can figure out how to do a label with a fluent string with a fixed message,
+            // or a label with a dynamic value in a hard-coded string, but I haven't figured out
+            // how to combine the two. 😢
+            diag.span_label(span, format!("generic parameter `{}` is unused", name));
+        }
+        diag
+    }
+}
+
+#[derive(LintDiagnostic)]
+#[diag(monomorphize::large_assignments)]
+#[note]
+pub struct LargeAssignmentsLint {
+    #[label]
+    pub span: Span,
+    pub size: u64,
+    pub limit: u64,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::unknown_partition_strategy)]
+pub struct UnknownPartitionStrategy;
+
+#[derive(SessionDiagnostic)]
+#[diag(monomorphize::symbol_already_defined)]
+pub struct SymbolAlreadyDefined {
+    #[primary_span]
+    pub span: Option<Span>,
+    pub symbol: String,
+}
diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs
index ef4560b5ec4..d64de44705b 100644
--- a/compiler/rustc_monomorphize/src/lib.rs
+++ b/compiler/rustc_monomorphize/src/lib.rs
@@ -3,6 +3,8 @@
 #![feature(let_else)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate tracing;
@@ -16,6 +18,7 @@ use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
 mod collector;
+mod errors;
 mod partitioning;
 mod polymorphize;
 mod util;
diff --git a/compiler/rustc_monomorphize/src/partitioning/mod.rs b/compiler/rustc_monomorphize/src/partitioning/mod.rs
index 43319dc5862..932edc6675f 100644
--- a/compiler/rustc_monomorphize/src/partitioning/mod.rs
+++ b/compiler/rustc_monomorphize/src/partitioning/mod.rs
@@ -108,6 +108,7 @@ use rustc_span::symbol::Symbol;
 
 use crate::collector::InliningMap;
 use crate::collector::{self, MonoItemCollectionMode};
+use crate::errors::{SymbolAlreadyDefined, UnknownPartitionStrategy};
 
 pub struct PartitioningCx<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -149,7 +150,9 @@ fn get_partitioner<'tcx>(tcx: TyCtxt<'tcx>) -> Box<dyn Partitioner<'tcx>> {
 
     match strategy {
         "default" => Box::new(default::DefaultPartitioning),
-        _ => tcx.sess.fatal("unknown partitioning strategy"),
+        _ => {
+            tcx.sess.emit_fatal(UnknownPartitionStrategy);
+        }
     }
 }
 
@@ -331,13 +334,7 @@ where
                 (span1, span2) => span1.or(span2),
             };
 
-            let error_message = format!("symbol `{}` is already defined", sym1);
-
-            if let Some(span) = span {
-                tcx.sess.span_fatal(span, &error_message)
-            } else {
-                tcx.sess.fatal(&error_message)
-            }
+            tcx.sess.emit_fatal(SymbolAlreadyDefined { span, symbol: sym1.to_string() });
         }
     }
 }
diff --git a/compiler/rustc_monomorphize/src/polymorphize.rs b/compiler/rustc_monomorphize/src/polymorphize.rs
index 394843e510d..6e4ab2a35c3 100644
--- a/compiler/rustc_monomorphize/src/polymorphize.rs
+++ b/compiler/rustc_monomorphize/src/polymorphize.rs
@@ -22,6 +22,8 @@ use rustc_span::symbol::sym;
 use std::convert::TryInto;
 use std::ops::ControlFlow;
 
+use crate::errors::UnusedGenericParams;
+
 /// Provide implementations of queries relating to polymorphization analysis.
 pub fn provide(providers: &mut Providers) {
     providers.unused_generic_params = unused_generic_params;
@@ -206,22 +208,23 @@ fn emit_unused_generic_params_error<'tcx>(
         _ => tcx.def_span(def_id),
     };
 
-    let mut err = tcx.sess.struct_span_err(fn_span, "item has unused generic parameters");
-
+    let mut param_spans = Vec::new();
+    let mut param_names = Vec::new();
     let mut next_generics = Some(generics);
     while let Some(generics) = next_generics {
         for param in &generics.params {
             if unused_parameters.contains(param.index).unwrap_or(false) {
                 debug!(?param);
                 let def_span = tcx.def_span(param.def_id);
-                err.span_label(def_span, &format!("generic parameter `{}` is unused", param.name));
+                param_spans.push(def_span);
+                param_names.push(param.name.to_string());
             }
         }
 
         next_generics = generics.parent.map(|did| tcx.generics_of(did));
     }
 
-    err.emit();
+    tcx.sess.emit_err(UnusedGenericParams { span: fn_span, param_spans, param_names });
 }
 
 /// Visitor used to aggregate generic parameter uses.
diff --git a/compiler/rustc_session/src/cgu_reuse_tracker.rs b/compiler/rustc_session/src/cgu_reuse_tracker.rs
index dd64e8ab71e..2a4a772f610 100644
--- a/compiler/rustc_session/src/cgu_reuse_tracker.rs
+++ b/compiler/rustc_session/src/cgu_reuse_tracker.rs
@@ -2,8 +2,13 @@
 //! compilation. This is used for incremental compilation tests and debug
 //! output.
 
+use crate::errors::{CguNotRecorded, IncorrectCguReuseType};
+use crate::Session;
 use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
 use rustc_span::{Span, Symbol};
+use std::borrow::Cow;
+use std::fmt::{self};
 use std::sync::{Arc, Mutex};
 use tracing::debug;
 
@@ -14,6 +19,22 @@ pub enum CguReuse {
     PostLto,
 }
 
+impl fmt::Display for CguReuse {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match *self {
+            CguReuse::No => write!(f, "No"),
+            CguReuse::PreLto => write!(f, "PreLto "),
+            CguReuse::PostLto => write!(f, "PostLto "),
+        }
+    }
+}
+
+impl IntoDiagnosticArg for CguReuse {
+    fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
+        DiagnosticArgValue::Str(Cow::Owned(self.to_string()))
+    }
+}
+
 #[derive(Copy, Clone, Debug, PartialEq)]
 pub enum ComparisonKind {
     Exact,
@@ -84,7 +105,7 @@ impl CguReuseTracker {
         }
     }
 
-    pub fn check_expected_reuse(&self, diag: &rustc_errors::Handler) {
+    pub fn check_expected_reuse(&self, sess: &Session) {
         if let Some(ref data) = self.data {
             let data = data.lock().unwrap();
 
@@ -98,19 +119,17 @@ impl CguReuseTracker {
                     };
 
                     if error {
-                        let at_least = if at_least { "at least " } else { "" };
-                        let msg = format!(
-                            "CGU-reuse for `{cgu_user_name}` is `{actual_reuse:?}` but \
-                                           should be {at_least}`{expected_reuse:?}`"
-                        );
-                        diag.span_err(error_span.0, &msg);
+                        let at_least = if at_least { 1 } else { 0 };
+                        IncorrectCguReuseType {
+                            span: error_span.0,
+                            cgu_user_name: &cgu_user_name,
+                            actual_reuse,
+                            expected_reuse,
+                            at_least,
+                        };
                     }
                 } else {
-                    let msg = format!(
-                        "CGU-reuse for `{cgu_user_name}` (mangled: `{cgu_name}`) was \
-                                       not recorded"
-                    );
-                    diag.span_fatal(error_span.0, &msg)
+                    sess.emit_fatal(CguNotRecorded { cgu_user_name, cgu_name });
                 }
             }
         }
diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs
new file mode 100644
index 00000000000..7252f1799da
--- /dev/null
+++ b/compiler/rustc_session/src/errors.rs
@@ -0,0 +1,45 @@
+use std::num::NonZeroU32;
+
+use crate as rustc_session;
+use crate::cgu_reuse_tracker::CguReuse;
+use rustc_errors::MultiSpan;
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[diag(session::incorrect_cgu_reuse_type)]
+pub struct IncorrectCguReuseType<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub cgu_user_name: &'a str,
+    pub actual_reuse: CguReuse,
+    pub expected_reuse: CguReuse,
+    pub at_least: u8,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::cgu_not_recorded)]
+pub struct CguNotRecorded<'a> {
+    pub cgu_user_name: &'a str,
+    pub cgu_name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(session::feature_gate_error, code = "E0658")]
+pub struct FeatureGateError<'a> {
+    #[primary_span]
+    pub span: MultiSpan,
+    pub explain: &'a str,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(session::feature_diagnostic_for_issue)]
+pub struct FeatureDiagnosticForIssue {
+    pub n: NonZeroU32,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[help(session::feature_diagnostic_help)]
+pub struct FeatureDiagnosticHelp {
+    pub feature: Symbol,
+}
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 7353c1ca0e2..429475c573c 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -12,6 +12,7 @@
 
 #[macro_use]
 extern crate rustc_macros;
+pub mod errors;
 
 pub mod cgu_reuse_tracker;
 pub mod utils;
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 17866dc6bdd..ebec754dcff 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -2,6 +2,7 @@
 //! It also serves as an input to the parser itself.
 
 use crate::config::CheckCfg;
+use crate::errors::{FeatureDiagnosticForIssue, FeatureDiagnosticHelp, FeatureGateError};
 use crate::lint::{
     builtin::UNSTABLE_SYNTAX_PRE_EXPANSION, BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId,
 };
@@ -11,7 +12,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{
-    error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
+    fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId,
     DiagnosticMessage, EmissionGuarantee, ErrorGuaranteed, MultiSpan, StashKey,
 };
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
@@ -112,7 +113,7 @@ pub fn feature_err_issue<'a>(
             .map(|err| err.cancel());
     }
 
-    let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
+    let mut err = sess.create_err(FeatureGateError { span, explain });
     add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
     err
 }
@@ -130,6 +131,8 @@ pub fn feature_warn<'a>(sess: &'a ParseSess, feature: Symbol, span: Span, explai
 ///
 /// This variant allows you to control whether it is a library or language feature.
 /// Almost always, you want to use this for a language feature. If so, prefer `feature_warn`.
+#[allow(rustc::diagnostic_outside_of_impl)]
+#[allow(rustc::untranslatable_diagnostic)]
 pub fn feature_warn_issue<'a>(
     sess: &'a ParseSess,
     feature: Symbol,
@@ -172,14 +175,12 @@ pub fn add_feature_diagnostics_for_issue<'a>(
     issue: GateIssue,
 ) {
     if let Some(n) = find_feature_issue(feature, issue) {
-        err.note(&format!(
-            "see issue #{n} <https://github.com/rust-lang/rust/issues/{n}> for more information"
-        ));
+        err.subdiagnostic(FeatureDiagnosticForIssue { n });
     }
 
     // #23973: do not suggest `#![feature(...)]` if we are in beta/stable
     if sess.unstable_features.is_nightly_build() {
-        err.help(&format!("add `#![feature({feature})]` to the crate attributes to enable"));
+        err.subdiagnostic(FeatureDiagnosticHelp { feature });
     }
 }
 
@@ -372,6 +373,8 @@ impl ParseSess {
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     pub fn struct_err(
         &self,
         msg: impl Into<DiagnosticMessage>,
@@ -380,16 +383,22 @@ impl ParseSess {
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
         self.span_diagnostic.struct_warn(msg)
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     pub fn struct_fatal(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, !> {
         self.span_diagnostic.struct_fatal(msg)
     }
 
     #[rustc_lint_diagnostics]
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    #[allow(rustc::untranslatable_diagnostic)]
     pub fn struct_diagnostic<G: EmissionGuarantee>(
         &self,
         msg: impl Into<DiagnosticMessage>,
diff --git a/compiler/rustc_symbol_mangling/Cargo.toml b/compiler/rustc_symbol_mangling/Cargo.toml
index b104a40c231..3db05225722 100644
--- a/compiler/rustc_symbol_mangling/Cargo.toml
+++ b/compiler/rustc_symbol_mangling/Cargo.toml
@@ -18,3 +18,5 @@ rustc_hir = { path = "../rustc_hir" }
 rustc_target = { path = "../rustc_target" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_session = { path = "../rustc_session" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_symbol_mangling/src/errors.rs b/compiler/rustc_symbol_mangling/src/errors.rs
new file mode 100644
index 00000000000..242997365a8
--- /dev/null
+++ b/compiler/rustc_symbol_mangling/src/errors.rs
@@ -0,0 +1,36 @@
+//! Errors emitted by symbol_mangling.
+
+use rustc_macros::SessionDiagnostic;
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_symbol_name)]
+pub struct InvalidSymbolName {
+    #[primary_span]
+    pub span: Span,
+    pub mangled_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_trait_item)]
+pub struct InvalidTraitItem {
+    #[primary_span]
+    pub span: Span,
+    pub demangling_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::alt_invalid_trait_item)]
+pub struct AltInvalidTraitItem {
+    #[primary_span]
+    pub span: Span,
+    pub alt_demangling_formatted: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(symbol_mangling::invalid_def_path)]
+pub struct InvalidDefPath {
+    #[primary_span]
+    pub span: Span,
+    pub def_path: String,
+}
diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs
index 5fc992023ca..0c6489acb34 100644
--- a/compiler/rustc_symbol_mangling/src/lib.rs
+++ b/compiler/rustc_symbol_mangling/src/lib.rs
@@ -91,6 +91,8 @@
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_middle;
@@ -110,6 +112,7 @@ use tracing::debug;
 mod legacy;
 mod v0;
 
+pub mod errors;
 pub mod test;
 pub mod typeid;
 
diff --git a/compiler/rustc_symbol_mangling/src/test.rs b/compiler/rustc_symbol_mangling/src/test.rs
index 7249ce04c15..b1c4cab11eb 100644
--- a/compiler/rustc_symbol_mangling/src/test.rs
+++ b/compiler/rustc_symbol_mangling/src/test.rs
@@ -4,6 +4,7 @@
 //! def-path. This is used for unit testing the code that generates
 //! paths etc in all kinds of annoying scenarios.
 
+use crate::errors::{AltInvalidTraitItem, InvalidDefPath, InvalidSymbolName, InvalidTraitItem};
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{subst::InternalSubsts, Instance, TyCtxt};
@@ -59,16 +60,27 @@ impl SymbolNamesTest<'_> {
                 tcx.erase_regions(InternalSubsts::identity_for_item(tcx, def_id)),
             );
             let mangled = tcx.symbol_name(instance);
-            tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
+            tcx.sess.emit_err(InvalidSymbolName {
+                span: attr.span,
+                mangled_formatted: format!("{mangled}"),
+            });
             if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
-                tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
-                tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
+                tcx.sess.emit_err(InvalidTraitItem {
+                    span: attr.span,
+                    demangling_formatted: format!("{demangling}"),
+                });
+                tcx.sess.emit_err(AltInvalidTraitItem {
+                    span: attr.span,
+                    alt_demangling_formatted: format!("{:#}", demangling),
+                });
             }
         }
 
         for attr in tcx.get_attrs(def_id.to_def_id(), DEF_PATH) {
-            let path = with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id()));
-            tcx.sess.span_err(attr.span, &format!("def-path({})", path));
+            tcx.sess.emit_err(InvalidDefPath {
+                span: attr.span,
+                def_path: with_no_trimmed_paths!(tcx.def_path_str(def_id.to_def_id())),
+            });
         }
     }
 }
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
index 2467a81638f..d08c0d4dbb7 100644
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -359,7 +359,7 @@ pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUn
     let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
 
     let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
-        tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err));
+        tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string()));
     });
 
     let source = tcx.type_of(impl_did);
diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs
index ed049194dd0..b4157fd5895 100644
--- a/library/alloc/src/vec/into_iter.rs
+++ b/library/alloc/src/vec/into_iter.rs
@@ -266,7 +266,7 @@ impl<T, A: Allocator> DoubleEndedIterator for IntoIter<T, A> {
             None
         } else if mem::size_of::<T>() == 0 {
             // See above for why 'ptr.offset' isn't used
-            self.end = self.ptr.wrapping_byte_sub(1);
+            self.end = self.end.wrapping_byte_sub(1);
 
             // Make up a value of this ZST.
             Some(unsafe { mem::zeroed() })
diff --git a/library/alloc/tests/vec.rs b/library/alloc/tests/vec.rs
index 4c118455a3f..f140fc4143f 100644
--- a/library/alloc/tests/vec.rs
+++ b/library/alloc/tests/vec.rs
@@ -1105,6 +1105,12 @@ fn test_into_iter_drop_allocator() {
 }
 
 #[test]
+fn test_into_iter_zst() {
+    for _ in vec![[0u64; 0]].into_iter() {}
+    for _ in vec![[0u64; 0]; 5].into_iter().rev() {}
+}
+
+#[test]
 fn test_from_iter_specialization() {
     let src: Vec<usize> = vec![0usize; 1];
     let srcptr = src.as_ptr();
diff --git a/src/doc/unstable-book/src/compiler-flags/sanitizer.md b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
index 7f7549aaf5a..b33405f18e9 100644
--- a/src/doc/unstable-book/src/compiler-flags/sanitizer.md
+++ b/src/doc/unstable-book/src/compiler-flags/sanitizer.md
@@ -9,17 +9,17 @@ The tracking issues for this feature are:
 
 This feature allows for use of one of following sanitizers:
 
-* [AddressSanitizer][clang-asan] a fast memory error detector.
-* [ControlFlowIntegrity][clang-cfi] LLVM Control Flow Integrity (CFI) provides
+* [AddressSanitizer](#addresssanitizer) a fast memory error detector.
+* [ControlFlowIntegrity](#controlflowintegrity) LLVM Control Flow Integrity (CFI) provides
   forward-edge control flow protection.
-* [HWAddressSanitizer][clang-hwasan] a memory error detector similar to
+* [HWAddressSanitizer](#hwaddresssanitizer) a memory error detector similar to
   AddressSanitizer, but based on partial hardware assistance.
-* [LeakSanitizer][clang-lsan] a run-time memory leak detector.
-* [MemorySanitizer][clang-msan] a detector of uninitialized reads.
-* [MemTagSanitizer][clang-memtag] fast memory error detector based on
+* [LeakSanitizer](#leaksanitizer) a run-time memory leak detector.
+* [MemorySanitizer](#memorysanitizer) a detector of uninitialized reads.
+* [MemTagSanitizer](#memtagsanitizer) fast memory error detector based on
   Armv8.5-A Memory Tagging Extension.
-* [ShadowCallStack][clang-scs] provides backward-edge control flow protection.
-* [ThreadSanitizer][clang-tsan] a fast data race detector.
+* [ShadowCallStack](#shadowcallstack) provides backward-edge control flow protection.
+* [ThreadSanitizer](#threadsanitizer) a fast data race detector.
 
 To enable a sanitizer compile with `-Zsanitizer=address`,`-Zsanitizer=cfi`,
 `-Zsanitizer=hwaddress`, `-Zsanitizer=leak`, `-Zsanitizer=memory`,
@@ -58,6 +58,8 @@ AddressSanitizer works with non-instrumented code although it will impede its
 ability to detect some bugs.  It is not expected to produce false positive
 reports.
 
+See the [Clang AddressSanitizer documentation][clang-asan] for more details.
+
 ## Examples
 
 Stack buffer overflow:
@@ -204,6 +206,8 @@ tracking issue [#89653](https://github.com/rust-lang/rust/issues/89653)).
 
 LLVM CFI can be enabled with -Zsanitizer=cfi and requires LTO (i.e., -Clto).
 
+See the [Clang ControlFlowIntegrity documentation][clang-cfi] for more details.
+
 ## Example
 
 ```text
@@ -430,6 +434,8 @@ HWAddressSanitizer requires `tagged-globals` target feature to instrument
 globals. To enable this target feature compile with `-C
 target-feature=+tagged-globals`
 
+See the [Clang HWAddressSanitizer documentation][clang-hwasan] for more details.
+
 ## Example
 
 Heap buffer overflow:
@@ -507,6 +513,8 @@ LeakSanitizer is supported on the following targets:
 * `x86_64-apple-darwin`
 * `x86_64-unknown-linux-gnu`
 
+See the [Clang LeakSanitizer documentation][clang-lsan] for more details.
+
 # MemorySanitizer
 
 MemorySanitizer is detector of uninitialized reads.
@@ -521,6 +529,8 @@ MemorySanitizer requires all program code to be instrumented. C/C++ dependencies
 need to be recompiled using Clang with `-fsanitize=memory` option. Failing to
 achieve that will result in false positive reports.
 
+See the [Clang MemorySanitizer documentation][clang-msan] for more details.
+
 ## Example
 
 Detecting the use of uninitialized memory. The `-Zbuild-std` flag rebuilds and
@@ -569,7 +579,7 @@ MemTagSanitizer is supported on the following targets:
 MemTagSanitizer requires hardware support and the `mte` target feature.
 To enable this target feature compile with `-C target-feature="+mte"`.
 
-More information can be found in the associated [LLVM documentation](https://llvm.org/docs/MemTagSanitizer.html).
+See the [LLVM MemTagSanitizer documentation][llvm-memtag] for more details.
 
 # ShadowCallStack
 
@@ -581,7 +591,9 @@ ShadowCallStack can be enabled with `-Zsanitizer=shadow-call-stack` option and i
 
 * `aarch64-linux-android`
 
-A runtime must be provided by the application or operating system. See the [LLVM documentation][clang-scs] for further details.
+A runtime must be provided by the application or operating system.
+
+See the [Clang ShadowCallStack documentation][clang-scs] for more details.
 
 # ThreadSanitizer
 
@@ -604,6 +616,8 @@ can lead to false positive reports.
 ThreadSanitizer does not support atomic fences `std::sync::atomic::fence`,
 nor synchronization performed using inline assembly code.
 
+See the [Clang ThreadSanitizer documentation][clang-tsan] for more details.
+
 ## Example
 
 ```rust
@@ -673,6 +687,7 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
 * [HWAddressSanitizer in Clang][clang-hwasan]
 * [LeakSanitizer in Clang][clang-lsan]
 * [MemorySanitizer in Clang][clang-msan]
+* [MemTagSanitizer in LLVM][llvm-memtag]
 * [ThreadSanitizer in Clang][clang-tsan]
 
 [clang-asan]: https://clang.llvm.org/docs/AddressSanitizer.html
@@ -682,3 +697,4 @@ Sanitizers produce symbolized stacktraces when llvm-symbolizer binary is in `PAT
 [clang-msan]: https://clang.llvm.org/docs/MemorySanitizer.html
 [clang-scs]: https://clang.llvm.org/docs/ShadowCallStack.html
 [clang-tsan]: https://clang.llvm.org/docs/ThreadSanitizer.html
+[llvm-memtag]: https://llvm.org/docs/MemTagSanitizer.html
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c425de081a6..41f9eb3cdf6 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -886,7 +886,10 @@ fn clean_function<'tcx>(
         // NOTE: generics must be cleaned before args
         let generics = clean_generics(generics, cx);
         let args = clean_args_from_types_and_body_id(cx, sig.decl.inputs, body_id);
-        let decl = clean_fn_decl_with_args(cx, sig.decl, args);
+        let mut decl = clean_fn_decl_with_args(cx, sig.decl, args);
+        if sig.header.is_async() {
+            decl.output = decl.sugared_async_return_type();
+        }
         (generics, decl)
     });
     Box::new(Function { decl, generics })
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index b023792e95a..6f49f00f93e 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -1310,22 +1310,19 @@ impl clean::FnDecl {
     ///   <br>Used to determine line-wrapping.
     /// * `indent`: The number of spaces to indent each successive line with, if line-wrapping is
     ///   necessary.
-    /// * `asyncness`: Whether the function is async or not.
     pub(crate) fn full_print<'a, 'tcx: 'a>(
         &'a self,
         header_len: usize,
         indent: usize,
-        asyncness: hir::IsAsync,
         cx: &'a Context<'tcx>,
     ) -> impl fmt::Display + 'a + Captures<'tcx> {
-        display_fn(move |f| self.inner_full_print(header_len, indent, asyncness, f, cx))
+        display_fn(move |f| self.inner_full_print(header_len, indent, f, cx))
     }
 
     fn inner_full_print(
         &self,
         header_len: usize,
         indent: usize,
-        asyncness: hir::IsAsync,
         f: &mut fmt::Formatter<'_>,
         cx: &Context<'_>,
     ) -> fmt::Result {
@@ -1390,15 +1387,9 @@ impl clean::FnDecl {
             args_plain.push_str(", ...");
         }
 
-        let arrow_plain;
-        let arrow = if let hir::IsAsync::Async = asyncness {
-            let output = self.sugared_async_return_type();
-            arrow_plain = format!("{:#}", output.print(cx));
-            if f.alternate() { arrow_plain.clone() } else { format!("{}", output.print(cx)) }
-        } else {
-            arrow_plain = format!("{:#}", self.output.print(cx));
-            if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) }
-        };
+        let arrow_plain = format!("{:#}", self.output.print(cx));
+        let arrow =
+            if f.alternate() { arrow_plain.clone() } else { format!("{}", self.output.print(cx)) };
 
         let declaration_len = header_len + args_plain.len() + arrow_plain.len();
         let output = if declaration_len > 80 {
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 6272f47f460..eff34047e3c 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -613,10 +613,10 @@ fn short_item_info(
 
 // Render the list of items inside one of the sections "Trait Implementations",
 // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages).
-fn render_impls(
+pub(crate) fn render_impls(
     cx: &mut Context<'_>,
     w: &mut Buffer,
-    impls: &[&&Impl],
+    impls: &[&Impl],
     containing_item: &clean::Item,
     toggle_open_by_default: bool,
 ) {
@@ -821,7 +821,7 @@ fn assoc_method(
         href = href,
         name = name,
         generics = g.print(cx),
-        decl = d.full_print(header_len, indent, header.asyncness, cx),
+        decl = d.full_print(header_len, indent, cx),
         notable_traits = notable_traits_decl(d, cx),
         where_clause = print_where_clause(g, cx, indent, end_newline),
     )
@@ -1025,6 +1025,47 @@ impl<'a> AssocItemLink<'a> {
     }
 }
 
+fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) {
+    write!(
+        w,
+        "<h2 id=\"{id}\" class=\"small-section-header\">\
+            {title}\
+            <a href=\"#{id}\" class=\"anchor\"></a>\
+         </h2>"
+    );
+}
+
+pub(crate) fn render_all_impls(
+    w: &mut Buffer,
+    cx: &mut Context<'_>,
+    containing_item: &clean::Item,
+    concrete: &[&Impl],
+    synthetic: &[&Impl],
+    blanket_impl: &[&Impl],
+) {
+    let mut impls = Buffer::empty_from(w);
+    render_impls(cx, &mut impls, concrete, containing_item, true);
+    let impls = impls.into_inner();
+    if !impls.is_empty() {
+        write_impl_section_heading(w, "Trait Implementations", "trait-implementations");
+        write!(w, "<div id=\"trait-implementations-list\">{}</div>", impls);
+    }
+
+    if !synthetic.is_empty() {
+        write_impl_section_heading(w, "Auto Trait Implementations", "synthetic-implementations");
+        w.write_str("<div id=\"synthetic-implementations-list\">");
+        render_impls(cx, w, synthetic, containing_item, false);
+        w.write_str("</div>");
+    }
+
+    if !blanket_impl.is_empty() {
+        write_impl_section_heading(w, "Blanket Implementations", "blanket-implementations");
+        w.write_str("<div id=\"blanket-implementations-list\">");
+        render_impls(cx, w, blanket_impl, containing_item, false);
+        w.write_str("</div>");
+    }
+}
+
 fn render_assoc_items(
     w: &mut Buffer,
     cx: &mut Context<'_>,
@@ -1054,12 +1095,7 @@ fn render_assoc_items_inner(
         let mut tmp_buf = Buffer::empty_from(w);
         let (render_mode, id) = match what {
             AssocItemRender::All => {
-                tmp_buf.write_str(
-                    "<h2 id=\"implementations\" class=\"small-section-header\">\
-                         Implementations\
-                         <a href=\"#implementations\" class=\"anchor\"></a>\
-                     </h2>",
-                );
+                write_impl_section_heading(&mut tmp_buf, "Implementations", "implementations");
                 (RenderMode::Normal, "implementations-list".to_owned())
             }
             AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => {
@@ -1068,15 +1104,14 @@ fn render_assoc_items_inner(
                 if let Some(def_id) = type_.def_id(cx.cache()) {
                     cx.deref_id_map.insert(def_id, id.clone());
                 }
-                write!(
-                    tmp_buf,
-                    "<h2 id=\"{id}\" class=\"small-section-header\">\
-                         <span>Methods from {trait_}&lt;Target = {type_}&gt;</span>\
-                         <a href=\"#{id}\" class=\"anchor\"></a>\
-                     </h2>",
-                    id = id,
-                    trait_ = trait_.print(cx),
-                    type_ = type_.print(cx),
+                write_impl_section_heading(
+                    &mut tmp_buf,
+                    &format!(
+                        "<span>Methods from {trait_}&lt;Target = {type_}&gt;</span>",
+                        trait_ = trait_.print(cx),
+                        type_ = type_.print(cx),
+                    ),
+                    &id,
                 );
                 (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id))
             }
@@ -1123,49 +1158,12 @@ fn render_assoc_items_inner(
             return;
         }
 
-        let (synthetic, concrete): (Vec<&&Impl>, Vec<&&Impl>) =
-            traits.iter().partition(|t| t.inner_impl().kind.is_auto());
-        let (blanket_impl, concrete): (Vec<&&Impl>, _) =
+        let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+            traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+        let (blanket_impl, concrete): (Vec<&Impl>, _) =
             concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
 
-        let mut impls = Buffer::empty_from(w);
-        render_impls(cx, &mut impls, &concrete, containing_item, true);
-        let impls = impls.into_inner();
-        if !impls.is_empty() {
-            write!(
-                w,
-                "<h2 id=\"trait-implementations\" class=\"small-section-header\">\
-                     Trait Implementations\
-                     <a href=\"#trait-implementations\" class=\"anchor\"></a>\
-                 </h2>\
-                 <div id=\"trait-implementations-list\">{}</div>",
-                impls
-            );
-        }
-
-        if !synthetic.is_empty() {
-            w.write_str(
-                "<h2 id=\"synthetic-implementations\" class=\"small-section-header\">\
-                     Auto Trait Implementations\
-                     <a href=\"#synthetic-implementations\" class=\"anchor\"></a>\
-                 </h2>\
-                 <div id=\"synthetic-implementations-list\">",
-            );
-            render_impls(cx, w, &synthetic, containing_item, false);
-            w.write_str("</div>");
-        }
-
-        if !blanket_impl.is_empty() {
-            w.write_str(
-                "<h2 id=\"blanket-implementations\" class=\"small-section-header\">\
-                     Blanket Implementations\
-                     <a href=\"#blanket-implementations\" class=\"anchor\"></a>\
-                 </h2>\
-                 <div id=\"blanket-implementations-list\">",
-            );
-            render_impls(cx, w, &blanket_impl, containing_item, false);
-            w.write_str("</div>");
-        }
+        render_all_impls(w, cx, containing_item, &concrete, &synthetic, &blanket_impl);
     }
 }
 
@@ -1970,6 +1968,70 @@ fn small_url_encode(s: String) -> String {
     }
 }
 
+pub(crate) fn sidebar_render_assoc_items(
+    cx: &Context<'_>,
+    out: &mut Buffer,
+    id_map: &mut IdMap,
+    concrete: Vec<&Impl>,
+    synthetic: Vec<&Impl>,
+    blanket_impl: Vec<&Impl>,
+) {
+    let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
+        let mut links = FxHashSet::default();
+
+        let mut ret = impls
+            .iter()
+            .filter_map(|it| {
+                let trait_ = it.inner_impl().trait_.as_ref()?;
+                let encoded =
+                    id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
+
+                let i_display = format!("{:#}", trait_.print(cx));
+                let out = Escape(&i_display);
+                let prefix = match it.inner_impl().polarity {
+                    ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
+                    ty::ImplPolarity::Negative => "!",
+                };
+                let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
+                if links.insert(generated.clone()) { Some(generated) } else { None }
+            })
+            .collect::<Vec<String>>();
+        ret.sort();
+        ret
+    };
+
+    let concrete_format = format_impls(concrete, id_map);
+    let synthetic_format = format_impls(synthetic, id_map);
+    let blanket_format = format_impls(blanket_impl, id_map);
+
+    if !concrete_format.is_empty() {
+        print_sidebar_block(
+            out,
+            "trait-implementations",
+            "Trait Implementations",
+            concrete_format.iter(),
+        );
+    }
+
+    if !synthetic_format.is_empty() {
+        print_sidebar_block(
+            out,
+            "synthetic-implementations",
+            "Auto Trait Implementations",
+            synthetic_format.iter(),
+        );
+    }
+
+    if !blanket_format.is_empty() {
+        print_sidebar_block(
+            out,
+            "blanket-implementations",
+            "Blanket Implementations",
+            blanket_format.iter(),
+        );
+    }
+}
+
 fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
     let did = it.item_id.expect_def_id();
     let cache = cx.cache();
@@ -2018,65 +2080,12 @@ fn sidebar_assoc_items(cx: &Context<'_>, out: &mut Buffer, it: &clean::Item) {
                 sidebar_deref_methods(cx, out, impl_, v, &mut derefs, &mut used_links);
             }
 
-            let format_impls = |impls: Vec<&Impl>, id_map: &mut IdMap| {
-                let mut links = FxHashSet::default();
-
-                let mut ret = impls
-                    .iter()
-                    .filter_map(|it| {
-                        let trait_ = it.inner_impl().trait_.as_ref()?;
-                        let encoded =
-                            id_map.derive(get_id_for_impl(&it.inner_impl().for_, Some(trait_), cx));
-
-                        let i_display = format!("{:#}", trait_.print(cx));
-                        let out = Escape(&i_display);
-                        let prefix = match it.inner_impl().polarity {
-                            ty::ImplPolarity::Positive | ty::ImplPolarity::Reservation => "",
-                            ty::ImplPolarity::Negative => "!",
-                        };
-                        let generated = format!("<a href=\"#{}\">{}{}</a>", encoded, prefix, out);
-                        if links.insert(generated.clone()) { Some(generated) } else { None }
-                    })
-                    .collect::<Vec<String>>();
-                ret.sort();
-                ret
-            };
-
             let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
                 v.iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_auto());
             let (blanket_impl, concrete): (Vec<&Impl>, Vec<&Impl>) =
                 concrete.into_iter().partition::<Vec<_>, _>(|i| i.inner_impl().kind.is_blanket());
 
-            let concrete_format = format_impls(concrete, &mut id_map);
-            let synthetic_format = format_impls(synthetic, &mut id_map);
-            let blanket_format = format_impls(blanket_impl, &mut id_map);
-
-            if !concrete_format.is_empty() {
-                print_sidebar_block(
-                    out,
-                    "trait-implementations",
-                    "Trait Implementations",
-                    concrete_format.iter(),
-                );
-            }
-
-            if !synthetic_format.is_empty() {
-                print_sidebar_block(
-                    out,
-                    "synthetic-implementations",
-                    "Auto Trait Implementations",
-                    synthetic_format.iter(),
-                );
-            }
-
-            if !blanket_format.is_empty() {
-                print_sidebar_block(
-                    out,
-                    "blanket-implementations",
-                    "Blanket Implementations",
-                    blanket_format.iter(),
-                );
-            }
+            sidebar_render_assoc_items(cx, out, &mut id_map, concrete, synthetic, blanket_impl);
         }
     }
 }
@@ -2346,9 +2355,54 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean
     buf.push_str("</section>")
 }
 
+/// Returns the list of implementations for the primitive reference type, filtering out any
+/// implementations that are on concrete or partially generic types, only keeping implementations
+/// of the form `impl<T> Trait for &T`.
+pub(crate) fn get_filtered_impls_for_reference<'a>(
+    shared: &'a Rc<SharedContext<'_>>,
+    it: &clean::Item,
+) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) {
+    let def_id = it.item_id.expect_def_id();
+    // If the reference primitive is somehow not defined, exit early.
+    let Some(v) = shared.cache.impls.get(&def_id) else { return (Vec::new(), Vec::new(), Vec::new()) };
+    // Since there is no "direct implementation" on the reference primitive type, we filter out
+    // every implementation which isn't a trait implementation.
+    let traits: Vec<_> = v.iter().filter(|i| i.inner_impl().trait_.is_some()).collect();
+    let (synthetic, concrete): (Vec<&Impl>, Vec<&Impl>) =
+        traits.into_iter().partition(|t| t.inner_impl().kind.is_auto());
+
+    let (blanket_impl, concrete): (Vec<&Impl>, _) =
+        concrete.into_iter().partition(|t| t.inner_impl().kind.is_blanket());
+    // Now we keep only references over full generic types.
+    let concrete: Vec<_> = concrete
+        .into_iter()
+        .filter(|t| match t.inner_impl().for_ {
+            clean::Type::BorrowedRef { ref type_, .. } => type_.is_full_generic(),
+            _ => false,
+        })
+        .collect();
+
+    (concrete, synthetic, blanket_impl)
+}
+
 fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) {
     let mut sidebar = Buffer::new();
-    sidebar_assoc_items(cx, &mut sidebar, it);
+
+    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+        sidebar_assoc_items(cx, &mut sidebar, it);
+    } else {
+        let shared = Rc::clone(&cx.shared);
+        let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+        sidebar_render_assoc_items(
+            cx,
+            &mut sidebar,
+            &mut IdMap::new(),
+            concrete,
+            synthetic,
+            blanket_impl,
+        );
+    }
 
     if !sidebar.is_empty() {
         write!(buf, "<section>{}</section>", sidebar.into_inner());
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 710064a37d0..d63d4c2d159 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -16,10 +16,10 @@ use std::fmt;
 use std::rc::Rc;
 
 use super::{
-    collect_paths_for_type, document, ensure_trailing_slash, item_ty_to_section,
-    notable_traits_decl, render_assoc_item, render_assoc_items, render_attributes_in_code,
-    render_attributes_in_pre, render_impl, render_rightside, render_stability_since_raw,
-    AssocItemLink, Context, ImplRenderingParameters,
+    collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference,
+    item_ty_to_section, notable_traits_decl, render_all_impls, render_assoc_item,
+    render_assoc_items, render_attributes_in_code, render_attributes_in_pre, render_impl,
+    render_rightside, render_stability_since_raw, AssocItemLink, Context, ImplRenderingParameters,
 };
 use crate::clean;
 use crate::config::ModuleSorting;
@@ -530,7 +530,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle
                 name = name,
                 generics = f.generics.print(cx),
                 where_clause = print_where_clause(&f.generics, cx, 0, Ending::Newline),
-                decl = f.decl.full_print(header_len, 0, header.asyncness, cx),
+                decl = f.decl.full_print(header_len, 0, cx),
                 notable_traits = notable_traits_decl(&f.decl, cx),
             );
         });
@@ -1371,8 +1371,18 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c
 }
 
 fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) {
+    let def_id = it.item_id.expect_def_id();
     document(w, cx, it, None, HeadingOffset::H2);
-    render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All)
+    if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) {
+        render_assoc_items(w, cx, it, def_id, AssocItemRender::All);
+    } else {
+        // We handle the "reference" primitive type on its own because we only want to list
+        // implementations on generic types.
+        let shared = Rc::clone(&cx.shared);
+        let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it);
+
+        render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl);
+    }
 }
 
 fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) {
diff --git a/src/test/rustdoc-json/fns/async_return.rs b/src/test/rustdoc-json/fns/async_return.rs
new file mode 100644
index 00000000000..b89781ca92d
--- /dev/null
+++ b/src/test/rustdoc-json/fns/async_return.rs
@@ -0,0 +1,36 @@
+// edition:2021
+// ignore-tidy-linelength
+
+// Regression test for <https://github.com/rust-lang/rust/issues/101199>
+
+use std::future::Future;
+
+// @is "$.index[*][?(@.name=='get_int')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int')].inner.header.async" false
+pub fn get_int() -> i32 {
+    42
+}
+
+// @is "$.index[*][?(@.name=='get_int_async')].inner.decl.output" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_async')].inner.header.async" true
+pub async fn get_int_async() -> i32 {
+    42
+}
+
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.kind" '"impl_trait"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_future')].inner.header.async" false
+pub fn get_int_future() -> impl Future<Output = i32> {
+    async { 42 }
+}
+
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.kind" '"impl_trait"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.name" '"Future"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].name" '"Output"'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.decl.output.inner[0].trait_bound.trait.args.angle_bracketed.bindings[0].binding.equality.type" '{"inner": "i32", "kind": "primitive"}'
+// @is "$.index[*][?(@.name=='get_int_future_async')].inner.header.async" true
+pub async fn get_int_future_async() -> impl Future<Output = i32> {
+    async { 42 }
+}
diff --git a/src/test/rustdoc/primitive-reference.rs b/src/test/rustdoc/primitive-reference.rs
new file mode 100644
index 00000000000..5c119340609
--- /dev/null
+++ b/src/test/rustdoc/primitive-reference.rs
@@ -0,0 +1,37 @@
+#![crate_name = "foo"]
+
+#![feature(rustdoc_internals)]
+
+// @has foo/index.html
+// @has - '//h2[@id="primitives"]' 'Primitive Types'
+// @has - '//a[@href="primitive.reference.html"]' 'reference'
+// @has - '//div[@class="sidebar-elems"]//li/a' 'Primitive Types'
+// @has - '//div[@class="sidebar-elems"]//li/a/@href' '#primitives'
+// @has foo/primitive.reference.html
+// @has - '//a[@class="primitive"]' 'reference'
+// @has - '//span[@class="in-band"]' 'Primitive Type reference'
+// @has - '//section[@id="main-content"]//div[@class="docblock"]//p' 'this is a test!'
+
+// There should be only one implementation listed.
+// @count - '//*[@class="impl has-srclink"]' 1
+// @has - '//*[@id="impl-Foo%3C%26A%3E-for-%26B"]/*[@class="code-header in-band"]' \
+//        'impl<A, B> Foo<&A> for &B'
+#[doc(primitive = "reference")]
+/// this is a test!
+mod reference {}
+
+pub struct Bar;
+
+// This implementation should **not** show up.
+impl<T> From<&T> for Bar {
+    fn from(s: &T) -> Self {
+        Bar
+    }
+}
+
+pub trait Foo<T> {
+    fn stuff(&self, other: &T) {}
+}
+
+// This implementation should show up.
+impl<A, B> Foo<&A> for &B {}
diff --git a/src/test/ui/type_length_limit.stderr b/src/test/ui/type_length_limit.stderr
index 1508b84c1b6..84ac48b1e77 100644
--- a/src/test/ui/type_length_limit.stderr
+++ b/src/test/ui/type_length_limit.stderr
@@ -4,8 +4,8 @@ error: reached the type-length limit while instantiating `std::mem::drop::<Optio
 LL | pub fn drop<T>(_x: T) {}
    | ^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
    = help: consider adding a `#![type_length_limit="8"]` attribute to your crate
+   = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
 
 error: reached the type-length limit while instantiating `<[closure@std::rt::lang_start<()...e<()>>::call_once - shim(vtable)`
   --> $SRC_DIR/core/src/ops/function.rs:LL:COL
@@ -13,8 +13,8 @@ error: reached the type-length limit while instantiating `<[closure@std::rt::lan
 LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
    = help: consider adding a `#![type_length_limit="8"]` attribute to your crate
+   = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit/type_length_limit.long-type.txt'
 
 error: aborting due to 2 previous errors