about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJosh Triplett <josh@joshtriplett.org>2025-08-14 00:28:44 -0700
committerJosh Triplett <josh@joshtriplett.org>2025-08-20 15:01:13 -0700
commitb65fab62999e96acd3683826ffd4140091f185f2 (patch)
treee9884fd6a901f6528290370abbc48d52b4a7c0a8
parent040a98af70f0a7da03f3d5356531b28a2a7a77e4 (diff)
downloadrust-b65fab62999e96acd3683826ffd4140091f185f2.tar.gz
rust-b65fab62999e96acd3683826ffd4140091f185f2.zip
Move `IntoDiagArg` earlier in the dependency chains
`rustc_errors` depends on numerous crates, solely to implement its
`IntoDiagArg` trait on types from those crates. Many crates depend on
`rustc_errors`, and it's on the critical path.

We can't swap things around to make all of those crates depend on
`rustc_errors` instead, because `rustc_errors` would end up in
dependency cycles.

Instead, move `IntoDiagArg` into `rustc_error_messages`, which has far
fewer dependencies, and then have most of these crates depend on
`rustc_error_messages`.

This allows `rustc_errors` to drop dependencies on several crates,
including the large `rustc_target`.

(This doesn't fully reduce dependency chains yet, as `rustc_errors`
still depends on `rustc_hir` which depends on `rustc_target`. That will
get fixed in a subsequent commit.)
-rw-r--r--Cargo.lock10
-rw-r--r--compiler/rustc_abi/Cargo.toml2
-rw-r--r--compiler/rustc_abi/src/extern_abi.rs3
-rw-r--r--compiler/rustc_error_messages/Cargo.toml2
-rw-r--r--compiler/rustc_error_messages/src/diagnostic_impls.rs205
-rw-r--r--compiler/rustc_error_messages/src/lib.rs53
-rw-r--r--compiler/rustc_errors/Cargo.toml4
-rw-r--r--compiler/rustc_errors/src/codes.rs2
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs52
-rw-r--r--compiler/rustc_errors/src/diagnostic_impls.rs326
-rw-r--r--compiler/rustc_errors/src/lib.rs18
-rw-r--r--compiler/rustc_hir/Cargo.toml1
-rw-r--r--compiler/rustc_hir/src/attrs/data_structures.rs26
-rw-r--r--compiler/rustc_hir/src/def.rs14
-rw-r--r--compiler/rustc_hir/src/hir.rs13
-rw-r--r--compiler/rustc_hir/src/target.rs2
-rw-r--r--compiler/rustc_hir/src/version.rs8
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs10
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs4
-rw-r--r--compiler/rustc_target/Cargo.toml1
-rw-r--r--compiler/rustc_target/src/spec/mod.rs13
-rw-r--r--compiler/rustc_type_ir/Cargo.toml2
-rw-r--r--compiler/rustc_type_ir/src/ir_print.rs49
23 files changed, 425 insertions, 395 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 51427f57822..4a72b78f774 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3367,6 +3367,7 @@ dependencies = [
  "rand 0.9.2",
  "rand_xoshiro",
  "rustc_data_structures",
+ "rustc_error_messages",
  "rustc_hashes",
  "rustc_index",
  "rustc_macros",
@@ -3779,6 +3780,8 @@ dependencies = [
  "icu_locid",
  "icu_provider_adapters",
  "intl-memoizer",
+ "rustc_ast",
+ "rustc_ast_pretty",
  "rustc_baked_icu_data",
  "rustc_data_structures",
  "rustc_macros",
@@ -3795,8 +3798,6 @@ dependencies = [
  "annotate-snippets 0.11.5",
  "derive_setters",
  "rustc_abi",
- "rustc_ast",
- "rustc_ast_pretty",
  "rustc_data_structures",
  "rustc_error_codes",
  "rustc_error_messages",
@@ -3809,8 +3810,6 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
- "rustc_target",
- "rustc_type_ir",
  "serde",
  "serde_json",
  "termcolor",
@@ -3898,6 +3897,7 @@ dependencies = [
  "rustc_ast",
  "rustc_ast_pretty",
  "rustc_data_structures",
+ "rustc_error_messages",
  "rustc_hashes",
  "rustc_index",
  "rustc_macros",
@@ -4647,6 +4647,7 @@ dependencies = [
  "object 0.37.2",
  "rustc_abi",
  "rustc_data_structures",
+ "rustc_error_messages",
  "rustc_fs_util",
  "rustc_macros",
  "rustc_serialize",
@@ -4765,6 +4766,7 @@ dependencies = [
  "rustc-hash 2.1.1",
  "rustc_ast_ir",
  "rustc_data_structures",
+ "rustc_error_messages",
  "rustc_index",
  "rustc_macros",
  "rustc_serialize",
diff --git a/compiler/rustc_abi/Cargo.toml b/compiler/rustc_abi/Cargo.toml
index 5f9afc46a1a..83d96d8d04d 100644
--- a/compiler/rustc_abi/Cargo.toml
+++ b/compiler/rustc_abi/Cargo.toml
@@ -9,6 +9,7 @@ bitflags = "2.4.1"
 rand = { version = "0.9.0", default-features = false, optional = true }
 rand_xoshiro = { version = "0.7.0", optional = true }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_error_messages = { path = "../rustc_error_messages", optional = true }
 rustc_hashes = { path = "../rustc_hashes" }
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
@@ -24,6 +25,7 @@ default = ["nightly", "randomize"]
 # without depending on rustc_data_structures, rustc_macros and rustc_serialize
 nightly = [
     "dep:rustc_data_structures",
+    "dep:rustc_error_messages",
     "dep:rustc_macros",
     "dep:rustc_serialize",
     "dep:rustc_span",
diff --git a/compiler/rustc_abi/src/extern_abi.rs b/compiler/rustc_abi/src/extern_abi.rs
index 29a3678abf3..41d744e1946 100644
--- a/compiler/rustc_abi/src/extern_abi.rs
+++ b/compiler/rustc_abi/src/extern_abi.rs
@@ -223,6 +223,9 @@ impl StableOrd for ExternAbi {
     const THIS_IMPLEMENTATION_HAS_BEEN_TRIPLE_CHECKED: () = ();
 }
 
+#[cfg(feature = "nightly")]
+rustc_error_messages::into_diag_arg_using_display!(ExternAbi);
+
 impl ExternAbi {
     /// An ABI "like Rust"
     ///
diff --git a/compiler/rustc_error_messages/Cargo.toml b/compiler/rustc_error_messages/Cargo.toml
index 0951859fa53..552ad672752 100644
--- a/compiler/rustc_error_messages/Cargo.toml
+++ b/compiler/rustc_error_messages/Cargo.toml
@@ -11,6 +11,8 @@ icu_list = "1.2"
 icu_locid = "1.2"
 icu_provider_adapters = "1.2"
 intl-memoizer = "0.5.1"
+rustc_ast = { path = "../rustc_ast" }
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_baked_icu_data = { path = "../rustc_baked_icu_data" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_error_messages/src/diagnostic_impls.rs b/compiler/rustc_error_messages/src/diagnostic_impls.rs
new file mode 100644
index 00000000000..3b664cce577
--- /dev/null
+++ b/compiler/rustc_error_messages/src/diagnostic_impls.rs
@@ -0,0 +1,205 @@
+use std::backtrace::Backtrace;
+use std::borrow::Cow;
+use std::fmt;
+use std::num::ParseIntError;
+use std::path::{Path, PathBuf};
+use std::process::ExitStatus;
+
+use rustc_ast as ast;
+use rustc_ast_pretty::pprust;
+use rustc_span::edition::Edition;
+
+use crate::{DiagArgValue, IntoDiagArg};
+
+pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display);
+
+impl IntoDiagArg for DiagArgFromDisplay<'_> {
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self.0.to_string().into_diag_arg(path)
+    }
+}
+
+impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> {
+    fn from(t: &'a dyn fmt::Display) -> Self {
+        DiagArgFromDisplay(t)
+    }
+}
+
+impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> {
+    fn from(t: &'a T) -> Self {
+        DiagArgFromDisplay(t)
+    }
+}
+
+impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T {
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self.clone().into_diag_arg(path)
+    }
+}
+
+#[macro_export]
+macro_rules! into_diag_arg_using_display {
+    ($( $ty:ty ),+ $(,)?) => {
+        $(
+            impl $crate::IntoDiagArg for $ty {
+                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> $crate::DiagArgValue {
+                    self.to_string().into_diag_arg(path)
+                }
+            }
+        )+
+    }
+}
+
+macro_rules! into_diag_arg_for_number {
+    ($( $ty:ty ),+ $(,)?) => {
+        $(
+            impl $crate::IntoDiagArg for $ty {
+                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> $crate::DiagArgValue {
+                    // Convert to a string if it won't fit into `Number`.
+                    #[allow(irrefutable_let_patterns)]
+                    if let Ok(n) = TryInto::<i32>::try_into(self) {
+                        $crate::DiagArgValue::Number(n)
+                    } else {
+                        self.to_string().into_diag_arg(path)
+                    }
+                }
+            }
+        )+
+    }
+}
+
+into_diag_arg_using_display!(
+    ast::ParamKindOrd,
+    std::io::Error,
+    Box<dyn std::error::Error>,
+    std::num::NonZero<u32>,
+    Edition,
+    rustc_span::Ident,
+    rustc_span::MacroRulesNormalizedIdent,
+    ParseIntError,
+    ExitStatus,
+);
+
+into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
+
+impl IntoDiagArg for bool {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        if self {
+            DiagArgValue::Str(Cow::Borrowed("true"))
+        } else {
+            DiagArgValue::Str(Cow::Borrowed("false"))
+        }
+    }
+}
+
+impl IntoDiagArg for char {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
+    }
+}
+
+impl IntoDiagArg for Vec<char> {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::StrListSepByAnd(
+            self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(),
+        )
+    }
+}
+
+impl IntoDiagArg for rustc_span::Symbol {
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self.to_ident_string().into_diag_arg(path)
+    }
+}
+
+impl<'a> IntoDiagArg for &'a str {
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self.to_string().into_diag_arg(path)
+    }
+}
+
+impl IntoDiagArg for String {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self))
+    }
+}
+
+impl<'a> IntoDiagArg for Cow<'a, str> {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.into_owned()))
+    }
+}
+
+impl<'a> IntoDiagArg for &'a Path {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
+    }
+}
+
+impl IntoDiagArg for PathBuf {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
+    }
+}
+
+impl IntoDiagArg for ast::Expr {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
+    }
+}
+
+impl IntoDiagArg for ast::Path {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))
+    }
+}
+
+impl IntoDiagArg for ast::token::Token {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(pprust::token_to_string(&self))
+    }
+}
+
+impl IntoDiagArg for ast::token::TokenKind {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(pprust::token_kind_to_string(&self))
+    }
+}
+
+impl IntoDiagArg for std::ffi::CString {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
+    }
+}
+
+impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
+    }
+}
+
+impl IntoDiagArg for ast::Visibility {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        let s = pprust::vis_to_string(&self);
+        let s = s.trim_end().to_string();
+        DiagArgValue::Str(Cow::Owned(s))
+    }
+}
+
+impl IntoDiagArg for Backtrace {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::from(self.to_string()))
+    }
+}
+
+impl IntoDiagArg for ast::util::parser::ExprPrecedence {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Number(self as i32)
+    }
+}
+
+impl IntoDiagArg for ast::FloatTy {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Borrowed(self.name_str()))
+    }
+}
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 4b3ecad307f..d8bacbe762b 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -23,6 +23,9 @@ use rustc_span::Span;
 use tracing::{instrument, trace};
 pub use unic_langid::{LanguageIdentifier, langid};
 
+mod diagnostic_impls;
+pub use diagnostic_impls::DiagArgFromDisplay;
+
 pub type FluentBundle =
     IntoDynSyncSend<fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>>;
 
@@ -589,3 +592,53 @@ pub fn fluent_value_from_str_list_sep_by_and(l: Vec<Cow<'_, str>>) -> FluentValu
 
     FluentValue::Custom(Box::new(FluentStrListSepByAnd(l)))
 }
+
+/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
+/// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic
+/// emission.
+pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue);
+
+/// Name of a diagnostic argument.
+pub type DiagArgName = Cow<'static, str>;
+
+/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
+/// to a `FluentValue` by the emitter to be used in diagnostic translation.
+#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
+pub enum DiagArgValue {
+    Str(Cow<'static, str>),
+    // This gets converted to a `FluentNumber`, which is an `f64`. An `i32`
+    // safely fits in an `f64`. Any integers bigger than that will be converted
+    // to strings in `into_diag_arg` and stored using the `Str` variant.
+    Number(i32),
+    StrListSepByAnd(Vec<Cow<'static, str>>),
+}
+
+/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct).
+/// Implemented as a custom trait rather than `From` so that it is implemented on the type being
+/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to
+/// implement this.
+pub trait IntoDiagArg {
+    /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic.
+    ///
+    /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big
+    /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering
+    /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a
+    /// value has no shortening logic that could be used, the argument can be safely ignored.
+    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue;
+}
+
+impl IntoDiagArg for DiagArgValue {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        self
+    }
+}
+
+impl From<DiagArgValue> for FluentValue<'static> {
+    fn from(val: DiagArgValue) -> Self {
+        match val {
+            DiagArgValue::Str(s) => From::from(s),
+            DiagArgValue::Number(n) => From::from(n),
+            DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l),
+        }
+    }
+}
diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml
index 3e8cf6207ae..7fb3533f109 100644
--- a/compiler/rustc_errors/Cargo.toml
+++ b/compiler/rustc_errors/Cargo.toml
@@ -8,8 +8,6 @@ edition = "2024"
 annotate-snippets = "0.11"
 derive_setters = "0.1.6"
 rustc_abi = { path = "../rustc_abi" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_error_codes = { path = "../rustc_error_codes" }
 rustc_error_messages = { path = "../rustc_error_messages" }
@@ -22,8 +20,6 @@ rustc_lint_defs = { path = "../rustc_lint_defs" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
 rustc_span = { path = "../rustc_span" }
-rustc_target = { path = "../rustc_target" }
-rustc_type_ir = { path = "../rustc_type_ir" }
 serde = { version = "1.0.125", features = ["derive"] }
 serde_json = "1.0.59"
 termcolor = "1.2.0"
diff --git a/compiler/rustc_errors/src/codes.rs b/compiler/rustc_errors/src/codes.rs
index 947cf27ca79..787a8af99b1 100644
--- a/compiler/rustc_errors/src/codes.rs
+++ b/compiler/rustc_errors/src/codes.rs
@@ -20,6 +20,8 @@ impl fmt::Display for ErrCode {
     }
 }
 
+rustc_error_messages::into_diag_arg_using_display!(ErrCode);
+
 macro_rules! define_error_code_constants_and_diagnostics_table {
     ($($name:ident: $num:literal,)*) => (
         $(
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index e579370ce4e..183dceddd2c 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -8,7 +8,7 @@ use std::path::PathBuf;
 use std::thread::panicking;
 
 use rustc_data_structures::fx::FxIndexMap;
-use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
+use rustc_error_messages::{DiagArgName, DiagArgValue, IntoDiagArg};
 use rustc_lint_defs::{Applicability, LintExpectationId};
 use rustc_macros::{Decodable, Encodable};
 use rustc_span::source_map::Spanned;
@@ -22,26 +22,6 @@ use crate::{
     Suggestions,
 };
 
-/// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
-/// `DiagArg` are converted to `FluentArgs` (consuming the collection) at the start of diagnostic
-/// emission.
-pub type DiagArg<'iter> = (&'iter DiagArgName, &'iter DiagArgValue);
-
-/// Name of a diagnostic argument.
-pub type DiagArgName = Cow<'static, str>;
-
-/// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
-/// to a `FluentValue` by the emitter to be used in diagnostic translation.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
-pub enum DiagArgValue {
-    Str(Cow<'static, str>),
-    // This gets converted to a `FluentNumber`, which is an `f64`. An `i32`
-    // safely fits in an `f64`. Any integers bigger than that will be converted
-    // to strings in `into_diag_arg` and stored using the `Str` variant.
-    Number(i32),
-    StrListSepByAnd(Vec<Cow<'static, str>>),
-}
-
 pub type DiagArgMap = FxIndexMap<DiagArgName, DiagArgValue>;
 
 /// Trait for types that `Diag::emit` can return as a "guarantee" (or "proof")
@@ -143,36 +123,6 @@ where
     }
 }
 
-/// Converts a value of a type into a `DiagArg` (typically a field of an `Diag` struct).
-/// Implemented as a custom trait rather than `From` so that it is implemented on the type being
-/// converted rather than on `DiagArgValue`, which enables types from other `rustc_*` crates to
-/// implement this.
-pub trait IntoDiagArg {
-    /// Convert `Self` into a `DiagArgValue` suitable for rendering in a diagnostic.
-    ///
-    /// It takes a `path` where "long values" could be written to, if the `DiagArgValue` is too big
-    /// for displaying on the terminal. This path comes from the `Diag` itself. When rendering
-    /// values that come from `TyCtxt`, like `Ty<'_>`, they can use `TyCtxt::short_string`. If a
-    /// value has no shortening logic that could be used, the argument can be safely ignored.
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue;
-}
-
-impl IntoDiagArg for DiagArgValue {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self
-    }
-}
-
-impl From<DiagArgValue> for FluentValue<'static> {
-    fn from(val: DiagArgValue) -> Self {
-        match val {
-            DiagArgValue::Str(s) => From::from(s),
-            DiagArgValue::Number(n) => From::from(n),
-            DiagArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l),
-        }
-    }
-}
-
 /// Trait implemented by error types. This should not be implemented manually. Instead, use
 /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
 #[rustc_diagnostic_item = "Subdiagnostic"]
diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs
index 698b6b8d504..435d16a8380 100644
--- a/compiler/rustc_errors/src/diagnostic_impls.rs
+++ b/compiler/rustc_errors/src/diagnostic_impls.rs
@@ -1,340 +1,22 @@
-use std::backtrace::Backtrace;
 use std::borrow::Cow;
-use std::fmt;
-use std::num::ParseIntError;
-use std::path::{Path, PathBuf};
-use std::process::ExitStatus;
 
 use rustc_abi::TargetDataLayoutErrors;
-use rustc_ast::util::parser::ExprPrecedence;
-use rustc_ast_pretty::pprust;
-use rustc_hir::RustcVersion;
-use rustc_hir::attrs::{MirDialect, MirPhase};
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
 use rustc_macros::Subdiagnostic;
-use rustc_span::edition::Edition;
-use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
-use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple};
-use rustc_type_ir::{ClosureKind, FloatTy};
-use {rustc_ast as ast, rustc_hir as hir};
+use rustc_span::{Span, Symbol};
 
 use crate::diagnostic::DiagLocation;
 use crate::{
-    Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level,
-    Subdiagnostic, fluent_generated as fluent,
+    Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, Subdiagnostic,
+    fluent_generated as fluent,
 };
 
-pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display);
-
-impl IntoDiagArg for DiagArgFromDisplay<'_> {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.0.to_string().into_diag_arg(path)
-    }
-}
-
-impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> {
-    fn from(t: &'a dyn fmt::Display) -> Self {
-        DiagArgFromDisplay(t)
-    }
-}
-
-impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> {
-    fn from(t: &'a T) -> Self {
-        DiagArgFromDisplay(t)
-    }
-}
-
-impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.clone().into_diag_arg(path)
-    }
-}
-
-#[macro_export]
-macro_rules! into_diag_arg_using_display {
-    ($( $ty:ty ),+ $(,)?) => {
-        $(
-            impl IntoDiagArg for $ty {
-                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-                    self.to_string().into_diag_arg(path)
-                }
-            }
-        )+
-    }
-}
-
-macro_rules! into_diag_arg_for_number {
-    ($( $ty:ty ),+ $(,)?) => {
-        $(
-            impl IntoDiagArg for $ty {
-                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-                    // Convert to a string if it won't fit into `Number`.
-                    #[allow(irrefutable_let_patterns)]
-                    if let Ok(n) = TryInto::<i32>::try_into(self) {
-                        DiagArgValue::Number(n)
-                    } else {
-                        self.to_string().into_diag_arg(path)
-                    }
-                }
-            }
-        )+
-    }
-}
-
-into_diag_arg_using_display!(
-    ast::ParamKindOrd,
-    std::io::Error,
-    Box<dyn std::error::Error>,
-    std::num::NonZero<u32>,
-    hir::Target,
-    Edition,
-    Ident,
-    MacroRulesNormalizedIdent,
-    ParseIntError,
-    StackProtector,
-    &TargetTuple,
-    SplitDebuginfo,
-    ExitStatus,
-    ErrCode,
-    rustc_abi::ExternAbi,
-);
-
-impl IntoDiagArg for RustcVersion {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.to_string()))
-    }
-}
-
-impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.to_string().into_diag_arg(path)
-    }
-}
-
-impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::ExistentialTraitRef<I> {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.to_string().into_diag_arg(path)
-    }
-}
-
-impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::UnevaluatedConst<I> {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        format!("{self:?}").into_diag_arg(path)
-    }
-}
-
-impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::FnSig<I> {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        format!("{self:?}").into_diag_arg(path)
-    }
-}
-
-impl<I: rustc_type_ir::Interner, T> IntoDiagArg for rustc_type_ir::Binder<I, T>
-where
-    T: IntoDiagArg,
-{
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.skip_binder().into_diag_arg(path)
-    }
-}
-
-into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
-
-impl IntoDiagArg for bool {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        if self {
-            DiagArgValue::Str(Cow::Borrowed("true"))
-        } else {
-            DiagArgValue::Str(Cow::Borrowed("false"))
-        }
-    }
-}
-
-impl IntoDiagArg for char {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
-    }
-}
-
-impl IntoDiagArg for Vec<char> {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::StrListSepByAnd(
-            self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(),
-        )
-    }
-}
-
-impl IntoDiagArg for Symbol {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.to_ident_string().into_diag_arg(path)
-    }
-}
-
-impl<'a> IntoDiagArg for &'a str {
-    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        self.to_string().into_diag_arg(path)
-    }
-}
-
-impl IntoDiagArg for String {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self))
-    }
-}
-
-impl<'a> IntoDiagArg for Cow<'a, str> {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.into_owned()))
-    }
-}
-
-impl<'a> IntoDiagArg for &'a Path {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
-    }
-}
-
-impl IntoDiagArg for PathBuf {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
-    }
-}
-
-impl IntoDiagArg for PanicStrategy {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.desc().to_string()))
-    }
-}
-
-impl IntoDiagArg for hir::ConstContext {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Borrowed(match self {
-            hir::ConstContext::ConstFn => "const_fn",
-            hir::ConstContext::Static(_) => "static",
-            hir::ConstContext::Const { .. } => "const",
-        }))
-    }
-}
-
-impl IntoDiagArg for ast::Expr {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
-    }
-}
-
-impl IntoDiagArg for ast::Path {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))
-    }
-}
-
-impl IntoDiagArg for ast::token::Token {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(pprust::token_to_string(&self))
-    }
-}
-
-impl IntoDiagArg for ast::token::TokenKind {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(pprust::token_kind_to_string(&self))
-    }
-}
-
-impl IntoDiagArg for FloatTy {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Borrowed(self.name_str()))
-    }
-}
-
-impl IntoDiagArg for std::ffi::CString {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
-    }
-}
-
-impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
-    }
-}
-
-impl IntoDiagArg for ast::Visibility {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        let s = pprust::vis_to_string(&self);
-        let s = s.trim_end().to_string();
-        DiagArgValue::Str(Cow::Owned(s))
-    }
-}
-
-impl IntoDiagArg for rustc_lint_defs::Level {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag()))
-    }
-}
-
-impl<Id> IntoDiagArg for hir::def::Res<Id> {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Borrowed(self.descr()))
-    }
-}
-
 impl IntoDiagArg for DiagLocation {
     fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
         DiagArgValue::Str(Cow::from(self.to_string()))
     }
 }
 
-impl IntoDiagArg for Backtrace {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::from(self.to_string()))
-    }
-}
-
-impl IntoDiagArg for Level {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::from(self.to_string()))
-    }
-}
-
-impl IntoDiagArg for ClosureKind {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(self.as_str().into())
-    }
-}
-
-impl IntoDiagArg for hir::def::Namespace {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Str(Cow::Borrowed(self.descr()))
-    }
-}
-
-impl IntoDiagArg for ExprPrecedence {
-    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
-        DiagArgValue::Number(self as i32)
-    }
-}
-
-impl IntoDiagArg for MirDialect {
-    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
-        let arg = match self {
-            MirDialect::Analysis => "analysis",
-            MirDialect::Built => "built",
-            MirDialect::Runtime => "runtime",
-        };
-        DiagArgValue::Str(Cow::Borrowed(arg))
-    }
-}
-
-impl IntoDiagArg for MirPhase {
-    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
-        let arg = match self {
-            MirPhase::Initial => "initial",
-            MirPhase::PostCleanup => "post-cleanup",
-            MirPhase::Optimized => "optimized",
-        };
-        DiagArgValue::Str(Cow::Borrowed(arg))
-    }
-}
-
 #[derive(Clone)]
 pub struct DiagSymbolList<S = Symbol>(Vec<S>);
 
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index 2534cddf105..0a2bfee8b29 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -41,12 +41,11 @@ use std::{fmt, panic};
 use Level::*;
 pub use codes::*;
 pub use diagnostic::{
-    BugAbort, Diag, DiagArg, DiagArgMap, DiagArgName, DiagArgValue, DiagInner, DiagStyledString,
-    Diagnostic, EmissionGuarantee, FatalAbort, IntoDiagArg, LintDiagnostic, StringPart, Subdiag,
-    Subdiagnostic,
+    BugAbort, Diag, DiagArgMap, DiagInner, DiagStyledString, Diagnostic, EmissionGuarantee,
+    FatalAbort, LintDiagnostic, StringPart, Subdiag, Subdiagnostic,
 };
 pub use diagnostic_impls::{
-    DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
+    DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
     IndicateAnonymousLifetime, SingleLabelManySpans,
 };
 pub use emitter::ColorConfig;
@@ -56,8 +55,9 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{DynSend, Lock};
 pub use rustc_error_messages::{
-    DiagMessage, FluentBundle, LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel,
-    SubdiagMessage, fallback_fluent_bundle, fluent_bundle,
+    DiagArg, DiagArgFromDisplay, DiagArgName, DiagArgValue, DiagMessage, FluentBundle, IntoDiagArg,
+    LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage,
+    fallback_fluent_bundle, fluent_bundle, into_diag_arg_using_display,
 };
 use rustc_hashes::Hash128;
 use rustc_hir::HirId;
@@ -1999,6 +1999,12 @@ impl Level {
     }
 }
 
+impl IntoDiagArg for Level {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::from(self.to_string()))
+    }
+}
+
 // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
 pub fn elided_lifetime_in_path_suggestion(
     source_map: &SourceMap,
diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml
index 71496b7ec32..592a9996c8f 100644
--- a/compiler/rustc_hir/Cargo.toml
+++ b/compiler/rustc_hir/Cargo.toml
@@ -12,6 +12,7 @@ rustc_arena = { path = "../rustc_arena" }
 rustc_ast = { path = "../rustc_ast" }
 rustc_ast_pretty = { path = "../rustc_ast_pretty" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_error_messages = { path = "../rustc_error_messages" }
 rustc_hashes = { path = "../rustc_hashes" }
 rustc_index = { path = "../rustc_index" }
 rustc_macros = { path = "../rustc_macros" }
diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs
index 31715955ed3..a17350f0392 100644
--- a/compiler/rustc_hir/src/attrs/data_structures.rs
+++ b/compiler/rustc_hir/src/attrs/data_structures.rs
@@ -1,7 +1,11 @@
+use std::borrow::Cow;
+use std::path::PathBuf;
+
 pub use ReprAttr::*;
 use rustc_abi::Align;
 use rustc_ast::token::CommentKind;
 use rustc_ast::{AttrStyle, ast};
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic, PrintAttribute};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::Transparency;
@@ -213,6 +217,17 @@ pub enum MirDialect {
     Runtime,
 }
 
+impl IntoDiagArg for MirDialect {
+    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
+        let arg = match self {
+            MirDialect::Analysis => "analysis",
+            MirDialect::Built => "built",
+            MirDialect::Runtime => "runtime",
+        };
+        DiagArgValue::Str(Cow::Borrowed(arg))
+    }
+}
+
 #[derive(Clone, Copy, Decodable, Debug, Encodable, PartialEq)]
 #[derive(HashStable_Generic, PrintAttribute)]
 pub enum MirPhase {
@@ -221,6 +236,17 @@ pub enum MirPhase {
     Optimized,
 }
 
+impl IntoDiagArg for MirPhase {
+    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
+        let arg = match self {
+            MirPhase::Initial => "initial",
+            MirPhase::PostCleanup => "post-cleanup",
+            MirPhase::Optimized => "optimized",
+        };
+        DiagArgValue::Str(Cow::Borrowed(arg))
+    }
+}
+
 /// Represents parsed *built-in* inert attributes.
 ///
 /// ## Overview
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 79319e24266..8af4740f376 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -1,10 +1,12 @@
 use std::array::IntoIter;
+use std::borrow::Cow;
 use std::fmt::Debug;
 
 use rustc_ast as ast;
 use rustc_ast::NodeId;
 use rustc_data_structures::stable_hasher::ToStableHashKey;
 use rustc_data_structures::unord::UnordMap;
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::Symbol;
 use rustc_span::def_id::{DefId, LocalDefId};
@@ -586,6 +588,12 @@ pub enum Res<Id = hir::HirId> {
     Err,
 }
 
+impl<Id> IntoDiagArg for Res<Id> {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Borrowed(self.descr()))
+    }
+}
+
 /// The result of resolving a path before lowering to HIR,
 /// with "module" segments resolved and associated item
 /// segments deferred to type checking.
@@ -673,6 +681,12 @@ impl Namespace {
     }
 }
 
+impl IntoDiagArg for Namespace {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Borrowed(self.descr()))
+    }
+}
+
 impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace {
     type KeyType = Namespace;
 
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 2c8986b7c7d..39696f74d51 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -1,4 +1,5 @@
 // ignore-tidy-filelength
+use std::borrow::Cow;
 use std::fmt;
 
 use rustc_abi::ExternAbi;
@@ -17,6 +18,7 @@ pub use rustc_ast::{
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::tagged_ptr::TaggedRef;
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
 use rustc_index::IndexVec;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_span::def_id::LocalDefId;
@@ -2259,8 +2261,15 @@ impl fmt::Display for ConstContext {
     }
 }
 
-// NOTE: `IntoDiagArg` impl for `ConstContext` lives in `rustc_errors`
-// due to a cyclical dependency between hir and that crate.
+impl IntoDiagArg for ConstContext {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Borrowed(match self {
+            ConstContext::ConstFn => "const_fn",
+            ConstContext::Static(_) => "static",
+            ConstContext::Const { .. } => "const",
+        }))
+    }
+}
 
 /// A literal.
 pub type Lit = Spanned<LitKind>;
diff --git a/compiler/rustc_hir/src/target.rs b/compiler/rustc_hir/src/target.rs
index f68dad3a5e8..dcac51b10b4 100644
--- a/compiler/rustc_hir/src/target.rs
+++ b/compiler/rustc_hir/src/target.rs
@@ -79,6 +79,8 @@ impl Display for Target {
     }
 }
 
+rustc_error_messages::into_diag_arg_using_display!(Target);
+
 impl Target {
     pub fn is_associated_item(self) -> bool {
         match self {
diff --git a/compiler/rustc_hir/src/version.rs b/compiler/rustc_hir/src/version.rs
index ab5ab026b4c..bc2c38a4935 100644
--- a/compiler/rustc_hir/src/version.rs
+++ b/compiler/rustc_hir/src/version.rs
@@ -1,6 +1,8 @@
+use std::borrow::Cow;
 use std::fmt::{self, Display};
 use std::sync::OnceLock;
 
+use rustc_error_messages::{DiagArgValue, IntoDiagArg};
 use rustc_macros::{
     Decodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
 };
@@ -45,3 +47,9 @@ impl Display for RustcVersion {
         write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
     }
 }
+
+impl IntoDiagArg for RustcVersion {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.to_string()))
+    }
+}
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 3bb7bbce567..94a290fa5b2 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 use rustc_abi::ExternAbi;
 use rustc_ast::AttrId;
 use rustc_ast::attr::AttributeExt;
@@ -6,7 +8,7 @@ use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_data_structures::stable_hasher::{
     HashStable, StableCompare, StableHasher, ToStableHashKey,
 };
-use rustc_error_messages::{DiagMessage, MultiSpan};
+use rustc_error_messages::{DiagArgValue, DiagMessage, IntoDiagArg, MultiSpan};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefPathHash;
 use rustc_hir::{HashStableContext, HirId, ItemLocalId};
@@ -297,6 +299,12 @@ impl Level {
     }
 }
 
+impl IntoDiagArg for Level {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag()))
+    }
+}
+
 /// Specification of a single lint.
 #[derive(Copy, Clone, Debug)]
 pub struct Lint {
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index b3042904a29..b9a6f67ab0d 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -23,7 +23,7 @@ impl IntoDiagArg for Ty<'_> {
     fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
         ty::tls::with(|tcx| {
             let ty = tcx.short_string(self, path);
-            rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(ty))
+            DiagArgValue::Str(std::borrow::Cow::Owned(ty))
         })
     }
 }
@@ -32,7 +32,7 @@ impl IntoDiagArg for Instance<'_> {
     fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> rustc_errors::DiagArgValue {
         ty::tls::with(|tcx| {
             let instance = tcx.short_string_namespace(self, path, Namespace::ValueNS);
-            rustc_errors::DiagArgValue::Str(std::borrow::Cow::Owned(instance))
+            DiagArgValue::Str(std::borrow::Cow::Owned(instance))
         })
     }
 }
diff --git a/compiler/rustc_target/Cargo.toml b/compiler/rustc_target/Cargo.toml
index 56932c24922..57c90a703f1 100644
--- a/compiler/rustc_target/Cargo.toml
+++ b/compiler/rustc_target/Cargo.toml
@@ -8,6 +8,7 @@ edition = "2024"
 bitflags = "2.4.1"
 rustc_abi = { path = "../rustc_abi" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_error_messages = { path = "../rustc_error_messages" }
 rustc_fs_util = { path = "../rustc_fs_util" }
 rustc_macros = { path = "../rustc_macros" }
 rustc_serialize = { path = "../rustc_serialize" }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index 2aa8ab5d317..399770022b2 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -50,6 +50,7 @@ use rustc_abi::{
     Align, CanonAbi, Endian, ExternAbi, Integer, Size, TargetDataLayout, TargetDataLayoutErrors,
 };
 use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
+use rustc_error_messages::{DiagArgValue, IntoDiagArg, into_diag_arg_using_display};
 use rustc_fs_util::try_canonicalize;
 use rustc_macros::{Decodable, Encodable, HashStable_Generic};
 use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
@@ -875,6 +876,12 @@ impl FromStr for PanicStrategy {
 
 crate::json::serde_deserialize_from_str!(PanicStrategy);
 
+impl IntoDiagArg for PanicStrategy {
+    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+        DiagArgValue::Str(Cow::Owned(self.desc().to_string()))
+    }
+}
+
 impl ToJson for PanicStrategy {
     fn to_json(&self) -> Json {
         match *self {
@@ -1518,6 +1525,8 @@ impl fmt::Display for SplitDebuginfo {
     }
 }
 
+into_diag_arg_using_display!(SplitDebuginfo);
+
 #[derive(Clone, Debug, PartialEq, Eq, serde_derive::Deserialize)]
 #[serde(tag = "kind")]
 #[serde(rename_all = "kebab-case")]
@@ -1795,6 +1804,8 @@ impl fmt::Display for StackProtector {
     }
 }
 
+into_diag_arg_using_display!(StackProtector);
+
 #[derive(PartialEq, Clone, Debug)]
 pub enum BinaryFormat {
     Coff,
@@ -3806,3 +3817,5 @@ impl fmt::Display for TargetTuple {
         write!(f, "{}", self.debug_tuple())
     }
 }
+
+into_diag_arg_using_display!(&TargetTuple);
diff --git a/compiler/rustc_type_ir/Cargo.toml b/compiler/rustc_type_ir/Cargo.toml
index e7fee574dd4..d55e9b3b1be 100644
--- a/compiler/rustc_type_ir/Cargo.toml
+++ b/compiler/rustc_type_ir/Cargo.toml
@@ -12,6 +12,7 @@ indexmap = "2.0.0"
 rustc-hash = "2.0.0"
 rustc_ast_ir = { path = "../rustc_ast_ir", default-features = false }
 rustc_data_structures = { path = "../rustc_data_structures", optional = true }
+rustc_error_messages = { path = "../rustc_error_messages", optional = true }
 rustc_index = { path = "../rustc_index", default-features = false }
 rustc_macros = { path = "../rustc_macros", optional = true }
 rustc_serialize = { path = "../rustc_serialize", optional = true }
@@ -27,6 +28,7 @@ tracing = "0.1"
 default = ["nightly"]
 nightly = [
     "dep:rustc_data_structures",
+    "dep:rustc_error_messages",
     "dep:rustc_macros",
     "dep:rustc_serialize",
     "dep:rustc_span",
diff --git a/compiler/rustc_type_ir/src/ir_print.rs b/compiler/rustc_type_ir/src/ir_print.rs
index 388ad09cb20..82bb8791b84 100644
--- a/compiler/rustc_type_ir/src/ir_print.rs
+++ b/compiler/rustc_type_ir/src/ir_print.rs
@@ -1,9 +1,9 @@
 use std::fmt;
 
 use crate::{
-    AliasTerm, AliasTy, Binder, CoercePredicate, ExistentialProjection, ExistentialTraitRef, FnSig,
-    HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate, PatternKind,
-    ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef,
+    AliasTerm, AliasTy, Binder, ClosureKind, CoercePredicate, ExistentialProjection,
+    ExistentialTraitRef, FnSig, HostEffectPredicate, Interner, NormalizesTo, OutlivesPredicate,
+    PatternKind, ProjectionPredicate, SubtypePredicate, TraitPredicate, TraitRef, UnevaluatedConst,
 };
 
 pub trait IrPrint<T> {
@@ -70,3 +70,46 @@ where
         <I as IrPrint<OutlivesPredicate<I, T>>>::print(self, fmt)
     }
 }
+
+#[cfg(feature = "nightly")]
+mod into_diag_arg_impls {
+    use rustc_error_messages::{DiagArgValue, IntoDiagArg};
+
+    use super::*;
+
+    impl<I: Interner> IntoDiagArg for TraitRef<I> {
+        fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            self.to_string().into_diag_arg(path)
+        }
+    }
+
+    impl<I: Interner> IntoDiagArg for ExistentialTraitRef<I> {
+        fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            self.to_string().into_diag_arg(path)
+        }
+    }
+
+    impl<I: Interner> IntoDiagArg for UnevaluatedConst<I> {
+        fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            format!("{self:?}").into_diag_arg(path)
+        }
+    }
+
+    impl<I: Interner> IntoDiagArg for FnSig<I> {
+        fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            format!("{self:?}").into_diag_arg(path)
+        }
+    }
+
+    impl<I: Interner, T: IntoDiagArg> IntoDiagArg for Binder<I, T> {
+        fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            self.skip_binder().into_diag_arg(path)
+        }
+    }
+
+    impl IntoDiagArg for ClosureKind {
+        fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
+            DiagArgValue::Str(self.as_str().into())
+        }
+    }
+}