about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast/src/util/literal.rs4
-rw-r--r--compiler/rustc_ast_lowering/Cargo.toml1
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs179
-rw-r--r--compiler/rustc_ast_lowering/src/errors.rs329
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs100
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs17
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs39
-rw-r--r--compiler/rustc_ast_lowering/src/pat.rs38
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs23
-rw-r--r--compiler/rustc_attr/src/builtin.rs330
-rw-r--r--compiler/rustc_attr/src/lib.rs3
-rw-r--r--compiler/rustc_attr/src/session_diagnostics.rs397
-rw-r--r--compiler/rustc_codegen_gcc/patches/0024-core-Disable-portable-simd-test.patch1
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs25
-rw-r--r--compiler/rustc_const_eval/src/const_eval/mod.rs21
-rw-r--r--compiler/rustc_const_eval/src/errors.rs107
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs156
-rw-r--r--compiler/rustc_driver/Cargo.toml1
-rw-r--r--compiler/rustc_driver/src/lib.rs35
-rw-r--r--compiler/rustc_driver/src/session_diagnostics.rs33
-rw-r--r--compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl131
-rw-r--r--compiler/rustc_error_messages/locales/en-US/attr.ftl107
-rw-r--r--compiler/rustc_error_messages/locales/en-US/const_eval.ftl52
-rw-r--r--compiler/rustc_error_messages/locales/en-US/driver.ftl11
-rw-r--r--compiler/rustc_error_messages/locales/en-US/lint.ftl34
-rw-r--r--compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl29
-rw-r--r--compiler/rustc_error_messages/locales/en-US/ty_utils.ftl47
-rw-r--r--compiler/rustc_error_messages/src/lib.rs5
-rw-r--r--compiler/rustc_lint/src/builtin.rs33
-rw-r--r--compiler/rustc_lint/src/context.rs93
-rw-r--r--compiler/rustc_lint/src/errors.rs162
-rw-r--r--compiler/rustc_lint/src/levels.rs98
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_middle/src/mir/mod.rs1
-rw-r--r--compiler/rustc_middle/src/thir.rs111
-rw-r--r--compiler/rustc_middle/src/thir/visit.rs19
-rw-r--r--compiler/rustc_mir_build/src/build/block.rs6
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_constant.rs18
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_place.rs12
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_rvalue.rs8
-rw-r--r--compiler/rustc_mir_build/src/build/expr/as_temp.rs5
-rw-r--r--compiler/rustc_mir_build/src/build/expr/into.rs17
-rw-r--r--compiler/rustc_mir_build/src/build/expr/stmt.rs22
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs13
-rw-r--r--compiler/rustc_mir_build/src/check_unsafety.rs6
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/block.rs8
-rw-r--r--compiler/rustc_mir_build/src/thir/cx/expr.rs60
-rw-r--r--compiler/rustc_mir_dataflow/Cargo.toml3
-rw-r--r--compiler/rustc_mir_dataflow/src/errors.rs71
-rw-r--r--compiler/rustc_mir_dataflow/src/framework/engine.rs13
-rw-r--r--compiler/rustc_mir_dataflow/src/impls/liveness.rs105
-rw-r--r--compiler/rustc_mir_dataflow/src/lib.rs3
-rw-r--r--compiler/rustc_mir_dataflow/src/rustc_peek.rs28
-rw-r--r--compiler/rustc_mir_transform/src/elaborate_box_derefs.rs23
-rw-r--r--compiler/rustc_mir_transform/src/generator.rs88
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs27
-rw-r--r--compiler/rustc_serialize/src/lib.rs1
-rw-r--r--compiler/rustc_serialize/src/serialize.rs26
-rw-r--r--compiler/rustc_session/src/config.rs7
-rw-r--r--compiler/rustc_session/src/session.rs21
-rw-r--r--compiler/rustc_target/src/spec/apple_base.rs8
-rw-r--r--compiler/rustc_target/src/spec/linux_base.rs8
-rw-r--r--compiler/rustc_target/src/spec/mod.rs104
-rw-r--r--compiler/rustc_target/src/spec/msvc_base.rs2
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs7
-rw-r--r--compiler/rustc_target/src/spec/windows_msvc_base.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/mod.rs13
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs5
-rw-r--r--compiler/rustc_transmute/src/layout/tree.rs18
-rw-r--r--compiler/rustc_transmute/src/lib.rs9
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml1
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs180
-rw-r--r--compiler/rustc_ty_utils/src/errors.rs69
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs3
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs7
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs33
76 files changed, 2643 insertions, 1191 deletions
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index 6a02a3b56f6..69a78d165ef 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -199,7 +199,9 @@ impl LitKind {
                 let symbol = if value { kw::True } else { kw::False };
                 (token::Bool, symbol, None)
             }
-            LitKind::Err => unreachable!(),
+            // This only shows up in places like `-Zunpretty=hir` output, so we
+            // don't bother to produce something useful.
+            LitKind::Err => (token::Err, Symbol::intern("<bad-literal>"), None),
         };
 
         token::Lit::new(kind, symbol, suffix)
diff --git a/compiler/rustc_ast_lowering/Cargo.toml b/compiler/rustc_ast_lowering/Cargo.toml
index 39ba62ef2ff..474aff2e2aa 100644
--- a/compiler/rustc_ast_lowering/Cargo.toml
+++ b/compiler/rustc_ast_lowering/Cargo.toml
@@ -15,6 +15,7 @@ rustc_target = { path = "../rustc_target" }
 rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_index = { path = "../rustc_index" }
 rustc_middle = { path = "../rustc_middle" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_query_system = { path = "../rustc_query_system" }
 rustc_span = { path = "../rustc_span" }
 rustc_errors = { path = "../rustc_errors" }
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 4166b4fc2e5..0dba9da63da 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -1,11 +1,17 @@
 use crate::{ImplTraitContext, ImplTraitPosition, ParamMode, ResolverAstLoweringExt};
 
+use super::errors::{
+    AbiSpecifiedMultipleTimes, AttSyntaxOnlyX86, ClobberAbiNotSupported,
+    InlineAsmUnsupportedTarget, InvalidAbiClobberAbi, InvalidAsmTemplateModifierConst,
+    InvalidAsmTemplateModifierRegClass, InvalidAsmTemplateModifierRegClassSub,
+    InvalidAsmTemplateModifierSym, InvalidRegister, InvalidRegisterClass, RegisterClassOnlyClobber,
+    RegisterConflict,
+};
 use super::LoweringContext;
 
 use rustc_ast::ptr::P;
 use rustc_ast::*;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::definitions::DefPathData;
@@ -26,13 +32,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let asm_arch =
             if self.tcx.sess.opts.actually_rustdoc { None } else { self.tcx.sess.asm_arch };
         if asm_arch.is_none() && !self.tcx.sess.opts.actually_rustdoc {
-            struct_span_err!(
-                self.tcx.sess,
-                sp,
-                E0472,
-                "inline assembly is unsupported on this target"
-            )
-            .emit();
+            self.tcx.sess.emit_err(InlineAsmUnsupportedTarget { span: sp });
         }
         if let Some(asm_arch) = asm_arch {
             // Inline assembly is currently only stable for these architectures.
@@ -59,10 +59,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64))
             && !self.tcx.sess.opts.actually_rustdoc
         {
-            self.tcx
-                .sess
-                .struct_span_err(sp, "the `att_syntax` option is only supported on x86")
-                .emit();
+            self.tcx.sess.emit_err(AttSyntaxOnlyX86 { span: sp });
         }
         if asm.options.contains(InlineAsmOptions::MAY_UNWIND) && !self.tcx.features().asm_unwind {
             feature_err(
@@ -82,51 +79,37 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         // If the abi was already in the list, emit an error
                         match clobber_abis.get(&abi) {
                             Some((prev_name, prev_sp)) => {
-                                let mut err = self.tcx.sess.struct_span_err(
-                                    *abi_span,
-                                    &format!("`{}` ABI specified multiple times", prev_name),
-                                );
-                                err.span_label(*prev_sp, "previously specified here");
-
                                 // Multiple different abi names may actually be the same ABI
                                 // If the specified ABIs are not the same name, alert the user that they resolve to the same ABI
                                 let source_map = self.tcx.sess.source_map();
-                                if source_map.span_to_snippet(*prev_sp)
-                                    != source_map.span_to_snippet(*abi_span)
-                                {
-                                    err.note("these ABIs are equivalent on the current target");
-                                }
+                                let equivalent = (source_map.span_to_snippet(*prev_sp)
+                                    != source_map.span_to_snippet(*abi_span))
+                                .then_some(());
 
-                                err.emit();
+                                self.tcx.sess.emit_err(AbiSpecifiedMultipleTimes {
+                                    abi_span: *abi_span,
+                                    prev_name: *prev_name,
+                                    prev_span: *prev_sp,
+                                    equivalent,
+                                });
                             }
                             None => {
-                                clobber_abis.insert(abi, (abi_name, *abi_span));
+                                clobber_abis.insert(abi, (*abi_name, *abi_span));
                             }
                         }
                     }
                     Err(&[]) => {
-                        self.tcx
-                            .sess
-                            .struct_span_err(
-                                *abi_span,
-                                "`clobber_abi` is not supported on this target",
-                            )
-                            .emit();
+                        self.tcx.sess.emit_err(ClobberAbiNotSupported { abi_span: *abi_span });
                     }
                     Err(supported_abis) => {
-                        let mut err = self
-                            .tcx
-                            .sess
-                            .struct_span_err(*abi_span, "invalid ABI for `clobber_abi`");
                         let mut abis = format!("`{}`", supported_abis[0]);
                         for m in &supported_abis[1..] {
                             let _ = write!(abis, ", `{}`", m);
                         }
-                        err.note(&format!(
-                            "the following ABIs are supported on this target: {}",
-                            abis
-                        ));
-                        err.emit();
+                        self.tcx.sess.emit_err(InvalidAbiClobberAbi {
+                            abi_span: *abi_span,
+                            supported_abis: abis,
+                        });
                     }
                 }
             }
@@ -141,24 +124,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             .iter()
             .map(|(op, op_sp)| {
                 let lower_reg = |reg| match reg {
-                    InlineAsmRegOrRegClass::Reg(s) => {
+                    InlineAsmRegOrRegClass::Reg(reg) => {
                         asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch {
-                            asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| {
-                                let msg = format!("invalid register `{}`: {}", s, e);
-                                sess.struct_span_err(*op_sp, &msg).emit();
+                            asm::InlineAsmReg::parse(asm_arch, reg).unwrap_or_else(|error| {
+                                sess.emit_err(InvalidRegister { op_span: *op_sp, reg, error });
                                 asm::InlineAsmReg::Err
                             })
                         } else {
                             asm::InlineAsmReg::Err
                         })
                     }
-                    InlineAsmRegOrRegClass::RegClass(s) => {
+                    InlineAsmRegOrRegClass::RegClass(reg_class) => {
                         asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch {
-                            asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| {
-                                let msg = format!("invalid register class `{}`: {}", s, e);
-                                sess.struct_span_err(*op_sp, &msg).emit();
-                                asm::InlineAsmRegClass::Err
-                            })
+                            asm::InlineAsmRegClass::parse(asm_arch, reg_class).unwrap_or_else(
+                                |error| {
+                                    sess.emit_err(InvalidRegisterClass {
+                                        op_span: *op_sp,
+                                        reg_class,
+                                        error,
+                                    });
+                                    asm::InlineAsmRegClass::Err
+                                },
+                            )
                         } else {
                             asm::InlineAsmRegClass::Err
                         })
@@ -282,50 +269,39 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         }
                         let valid_modifiers = class.valid_modifiers(asm_arch.unwrap());
                         if !valid_modifiers.contains(&modifier) {
-                            let mut err = sess.struct_span_err(
-                                placeholder_span,
-                                "invalid asm template modifier for this register class",
-                            );
-                            err.span_label(placeholder_span, "template modifier");
-                            err.span_label(op_sp, "argument");
-                            if !valid_modifiers.is_empty() {
+                            let sub = if !valid_modifiers.is_empty() {
                                 let mut mods = format!("`{}`", valid_modifiers[0]);
                                 for m in &valid_modifiers[1..] {
                                     let _ = write!(mods, ", `{}`", m);
                                 }
-                                err.note(&format!(
-                                    "the `{}` register class supports \
-                                     the following template modifiers: {}",
-                                    class.name(),
-                                    mods
-                                ));
+                                InvalidAsmTemplateModifierRegClassSub::SupportModifier {
+                                    class_name: class.name(),
+                                    modifiers: mods,
+                                }
                             } else {
-                                err.note(&format!(
-                                    "the `{}` register class does not support template modifiers",
-                                    class.name()
-                                ));
-                            }
-                            err.emit();
+                                InvalidAsmTemplateModifierRegClassSub::DoesNotSupportModifier {
+                                    class_name: class.name(),
+                                }
+                            };
+                            sess.emit_err(InvalidAsmTemplateModifierRegClass {
+                                placeholder_span,
+                                op_span: op_sp,
+                                sub,
+                            });
                         }
                     }
                     hir::InlineAsmOperand::Const { .. } => {
-                        let mut err = sess.struct_span_err(
+                        sess.emit_err(InvalidAsmTemplateModifierConst {
                             placeholder_span,
-                            "asm template modifiers are not allowed for `const` arguments",
-                        );
-                        err.span_label(placeholder_span, "template modifier");
-                        err.span_label(op_sp, "argument");
-                        err.emit();
+                            op_span: op_sp,
+                        });
                     }
                     hir::InlineAsmOperand::SymFn { .. }
                     | hir::InlineAsmOperand::SymStatic { .. } => {
-                        let mut err = sess.struct_span_err(
+                        sess.emit_err(InvalidAsmTemplateModifierSym {
                             placeholder_span,
-                            "asm template modifiers are not allowed for `sym` arguments",
-                        );
-                        err.span_label(placeholder_span, "template modifier");
-                        err.span_label(op_sp, "argument");
-                        err.emit();
+                            op_span: op_sp,
+                        });
                     }
                 }
             }
@@ -346,12 +322,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // require that the operand name an explicit register, not a
                 // register class.
                 if reg_class.is_clobber_only(asm_arch.unwrap()) && !op.is_clobber() {
-                    let msg = format!(
-                        "register class `{}` can only be used as a clobber, \
-                             not as an input or output",
-                        reg_class.name()
-                    );
-                    sess.struct_span_err(op_sp, &msg).emit();
+                    sess.emit_err(RegisterClassOnlyClobber {
+                        op_span: op_sp,
+                        reg_class_name: reg_class.name(),
+                    });
                     continue;
                 }
 
@@ -391,16 +365,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                         unreachable!();
                                     };
 
-                                    let msg = format!(
-                                        "register `{}` conflicts with register `{}`",
-                                        reg.name(),
-                                        reg2.name()
-                                    );
-                                    let mut err = sess.struct_span_err(op_sp, &msg);
-                                    err.span_label(op_sp, &format!("register `{}`", reg.name()));
-                                    err.span_label(op_sp2, &format!("register `{}`", reg2.name()));
-
-                                    match (op, op2) {
+                                    let in_out = match (op, op2) {
                                         (
                                             hir::InlineAsmOperand::In { .. },
                                             hir::InlineAsmOperand::Out { late, .. },
@@ -411,14 +376,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                         ) => {
                                             assert!(!*late);
                                             let out_op_sp = if input { op_sp2 } else { op_sp };
-                                            let msg = "use `lateout` instead of \
-                                                       `out` to avoid conflict";
-                                            err.span_help(out_op_sp, msg);
-                                        }
-                                        _ => {}
-                                    }
+                                            Some(out_op_sp)
+                                        },
+                                        _ => None,
+                                    };
 
-                                    err.emit();
+                                    sess.emit_err(RegisterConflict {
+                                        op_span1: op_sp,
+                                        op_span2: op_sp2,
+                                        reg1_name: reg.name(),
+                                        reg2_name: reg2.name(),
+                                        in_out
+                                    });
                                 }
                                 Entry::Vacant(v) => {
                                     if r == reg {
diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs
new file mode 100644
index 00000000000..59f1b7180e4
--- /dev/null
+++ b/compiler/rustc_ast_lowering/src/errors.rs
@@ -0,0 +1,329 @@
+use rustc_errors::{fluent, AddSubdiagnostic, Applicability, Diagnostic, DiagnosticArgFromDisplay};
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_span::{symbol::Ident, Span, Symbol};
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::generic_type_with_parentheses, code = "E0214")]
+pub struct GenericTypeWithParentheses {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sub: Option<UseAngleBrackets>,
+}
+
+#[derive(Clone, Copy)]
+pub struct UseAngleBrackets {
+    pub open_param: Span,
+    pub close_param: Span,
+}
+
+impl AddSubdiagnostic for UseAngleBrackets {
+    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+        diag.multipart_suggestion(
+            fluent::ast_lowering::use_angle_brackets,
+            vec![(self.open_param, String::from("<")), (self.close_param, String::from(">"))],
+            Applicability::MaybeIncorrect,
+        );
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[help]
+#[diag(ast_lowering::invalid_abi, code = "E0703")]
+pub struct InvalidAbi {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub abi: Symbol,
+    pub valid_abis: String,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::assoc_ty_parentheses)]
+pub struct AssocTyParentheses {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sub: AssocTyParenthesesSub,
+}
+
+#[derive(Clone, Copy)]
+pub enum AssocTyParenthesesSub {
+    Empty { parentheses_span: Span },
+    NotEmpty { open_param: Span, close_param: Span },
+}
+
+impl AddSubdiagnostic for AssocTyParenthesesSub {
+    fn add_to_diagnostic(self, diag: &mut Diagnostic) {
+        match self {
+            Self::Empty { parentheses_span } => diag.multipart_suggestion(
+                fluent::ast_lowering::remove_parentheses,
+                vec![(parentheses_span, String::new())],
+                Applicability::MaybeIncorrect,
+            ),
+            Self::NotEmpty { open_param, close_param } => diag.multipart_suggestion(
+                fluent::ast_lowering::use_angle_brackets,
+                vec![(open_param, String::from("<")), (close_param, String::from(">"))],
+                Applicability::MaybeIncorrect,
+            ),
+        };
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(ast_lowering::misplaced_impl_trait, code = "E0562")]
+pub struct MisplacedImplTrait<'a> {
+    #[primary_span]
+    pub span: Span,
+    pub position: DiagnosticArgFromDisplay<'a>,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::rustc_box_attribute_error)]
+pub struct RustcBoxAttributeError {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::underscore_expr_lhs_assign)]
+pub struct UnderscoreExprLhsAssign {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::base_expression_double_dot)]
+pub struct BaseExpressionDoubleDot {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::await_only_in_async_fn_and_blocks, code = "E0728")]
+pub struct AwaitOnlyInAsyncFnAndBlocks {
+    #[primary_span]
+    #[label]
+    pub dot_await_span: Span,
+    #[label(ast_lowering::this_not_async)]
+    pub item_span: Option<Span>,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::generator_too_many_parameters, code = "E0628")]
+pub struct GeneratorTooManyParameters {
+    #[primary_span]
+    pub fn_decl_span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::closure_cannot_be_static, code = "E0697")]
+pub struct ClosureCannotBeStatic {
+    #[primary_span]
+    pub fn_decl_span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[help]
+#[diag(ast_lowering::async_non_move_closure_not_supported, code = "E0708")]
+pub struct AsyncNonMoveClosureNotSupported {
+    #[primary_span]
+    pub fn_decl_span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::functional_record_update_destructuring_assignment)]
+pub struct FunctionalRecordUpdateDestructuringAssignemnt {
+    #[primary_span]
+    #[suggestion(code = "", applicability = "machine-applicable")]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::async_generators_not_supported, code = "E0727")]
+pub struct AsyncGeneratorsNotSupported {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::inline_asm_unsupported_target, code = "E0472")]
+pub struct InlineAsmUnsupportedTarget {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::att_syntax_only_x86)]
+pub struct AttSyntaxOnlyX86 {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::abi_specified_multiple_times)]
+pub struct AbiSpecifiedMultipleTimes {
+    #[primary_span]
+    pub abi_span: Span,
+    pub prev_name: Symbol,
+    #[label]
+    pub prev_span: Span,
+    #[note]
+    pub equivalent: Option<()>,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::clobber_abi_not_supported)]
+pub struct ClobberAbiNotSupported {
+    #[primary_span]
+    pub abi_span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[note]
+#[diag(ast_lowering::invalid_abi_clobber_abi)]
+pub struct InvalidAbiClobberAbi {
+    #[primary_span]
+    pub abi_span: Span,
+    pub supported_abis: String,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::invalid_register)]
+pub struct InvalidRegister<'a> {
+    #[primary_span]
+    pub op_span: Span,
+    pub reg: Symbol,
+    pub error: &'a str,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::invalid_register_class)]
+pub struct InvalidRegisterClass<'a> {
+    #[primary_span]
+    pub op_span: Span,
+    pub reg_class: Symbol,
+    pub error: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(ast_lowering::invalid_asm_template_modifier_reg_class)]
+pub struct InvalidAsmTemplateModifierRegClass {
+    #[primary_span]
+    #[label(ast_lowering::template_modifier)]
+    pub placeholder_span: Span,
+    #[label(ast_lowering::argument)]
+    pub op_span: Span,
+    #[subdiagnostic]
+    pub sub: InvalidAsmTemplateModifierRegClassSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum InvalidAsmTemplateModifierRegClassSub {
+    #[note(ast_lowering::support_modifiers)]
+    SupportModifier { class_name: Symbol, modifiers: String },
+    #[note(ast_lowering::does_not_support_modifiers)]
+    DoesNotSupportModifier { class_name: Symbol },
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::invalid_asm_template_modifier_const)]
+pub struct InvalidAsmTemplateModifierConst {
+    #[primary_span]
+    #[label(ast_lowering::template_modifier)]
+    pub placeholder_span: Span,
+    #[label(ast_lowering::argument)]
+    pub op_span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::invalid_asm_template_modifier_sym)]
+pub struct InvalidAsmTemplateModifierSym {
+    #[primary_span]
+    #[label(ast_lowering::template_modifier)]
+    pub placeholder_span: Span,
+    #[label(ast_lowering::argument)]
+    pub op_span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::register_class_only_clobber)]
+pub struct RegisterClassOnlyClobber {
+    #[primary_span]
+    pub op_span: Span,
+    pub reg_class_name: Symbol,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::register_conflict)]
+pub struct RegisterConflict<'a> {
+    #[primary_span]
+    #[label(ast_lowering::register1)]
+    pub op_span1: Span,
+    #[label(ast_lowering::register2)]
+    pub op_span2: Span,
+    pub reg1_name: &'a str,
+    pub reg2_name: &'a str,
+    #[help]
+    pub in_out: Option<Span>,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[help]
+#[diag(ast_lowering::sub_tuple_binding)]
+pub struct SubTupleBinding<'a> {
+    #[primary_span]
+    #[label]
+    #[suggestion_verbose(
+        ast_lowering::sub_tuple_binding_suggestion,
+        code = "..",
+        applicability = "maybe-incorrect"
+    )]
+    pub span: Span,
+    pub ident: Ident,
+    pub ident_name: Symbol,
+    pub ctx: &'a str,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::extra_double_dot)]
+pub struct ExtraDoubleDot<'a> {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[label(ast_lowering::previously_used_here)]
+    pub prev_span: Span,
+    pub ctx: &'a str,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[note]
+#[diag(ast_lowering::misplaced_double_dot)]
+pub struct MisplacedDoubleDot {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::misplaced_relax_trait_bound)]
+pub struct MisplacedRelaxTraitBound {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::not_supported_for_lifetime_binder_async_closure)]
+pub struct NotSupportedForLifetimeBinderAsyncClosure {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic, Clone, Copy)]
+#[diag(ast_lowering::arbitrary_expression_in_pattern)]
+pub struct ArbitraryExpressionInPattern {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index bd61f4fa87a..61f8c0216f1 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -1,3 +1,9 @@
+use super::errors::{
+    AsyncGeneratorsNotSupported, AsyncNonMoveClosureNotSupported, AwaitOnlyInAsyncFnAndBlocks,
+    BaseExpressionDoubleDot, ClosureCannotBeStatic, FunctionalRecordUpdateDestructuringAssignemnt,
+    GeneratorTooManyParameters, NotSupportedForLifetimeBinderAsyncClosure, RustcBoxAttributeError,
+    UnderscoreExprLhsAssign,
+};
 use super::ResolverAstLoweringExt;
 use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
 use crate::{FnDeclKind, ImplTraitPosition};
@@ -6,7 +12,6 @@ use rustc_ast::attr;
 use rustc_ast::ptr::P as AstP;
 use rustc_ast::*;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_hir::definitions::DefPathData;
@@ -45,13 +50,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                             let hir_id = self.lower_node_id(e.id);
                             return hir::Expr { hir_id, kind, span: self.lower_span(e.span) };
                         } else {
-                            self.tcx.sess
-                                .struct_span_err(
-                                    e.span,
-                                    "#[rustc_box] requires precisely one argument \
-                                    and no other attributes are allowed",
-                                )
-                                .emit();
+                            self.tcx.sess.emit_err(RustcBoxAttributeError { span: e.span });
                             hir::ExprKind::Err
                         }
                     } else if let Some(legacy_args) = self.resolver.legacy_const_generic_args(f) {
@@ -211,13 +210,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
                 }
                 ExprKind::Underscore => {
-                    self.tcx
-                        .sess.struct_span_err(
-                            e.span,
-                            "in expressions, `_` can only be used on the left-hand side of an assignment",
-                        )
-                        .span_label(e.span, "`_` not allowed here")
-                        .emit();
+                    self.tcx.sess.emit_err(UnderscoreExprLhsAssign { span: e.span });
                     hir::ExprKind::Err
                 }
                 ExprKind::Path(ref qself, ref path) => {
@@ -249,11 +242,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                     let rest = match &se.rest {
                         StructRest::Base(e) => Some(self.lower_expr(e)),
                         StructRest::Rest(sp) => {
-                            self.tcx
-                                .sess
-                                .struct_span_err(*sp, "base expression required after `..`")
-                                .span_label(*sp, "add a base expression here")
-                                .emit();
+                            self.tcx.sess.emit_err(BaseExpressionDoubleDot { span: *sp });
                             Some(&*self.arena.alloc(self.expr_err(*sp)))
                         }
                         StructRest::None => None,
@@ -662,17 +651,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         match self.generator_kind {
             Some(hir::GeneratorKind::Async(_)) => {}
             Some(hir::GeneratorKind::Gen) | None => {
-                let mut err = struct_span_err!(
-                    self.tcx.sess,
+                self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
                     dot_await_span,
-                    E0728,
-                    "`await` is only allowed inside `async` functions and blocks"
-                );
-                err.span_label(dot_await_span, "only allowed inside `async` functions and blocks");
-                if let Some(item_sp) = self.current_item {
-                    err.span_label(item_sp, "this is not `async`");
-                }
-                err.emit();
+                    item_span: self.current_item,
+                });
             }
         }
         let span = self.mark_span_with_reason(DesugaringKind::Await, dot_await_span, None);
@@ -892,13 +874,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         match generator_kind {
             Some(hir::GeneratorKind::Gen) => {
                 if decl.inputs.len() > 1 {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        fn_decl_span,
-                        E0628,
-                        "too many parameters for a generator (expected 0 or 1 parameters)"
-                    )
-                    .emit();
+                    self.tcx.sess.emit_err(GeneratorTooManyParameters { fn_decl_span });
                 }
                 Some(movability)
             }
@@ -907,13 +883,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             }
             None => {
                 if movability == Movability::Static {
-                    struct_span_err!(
-                        self.tcx.sess,
-                        fn_decl_span,
-                        E0697,
-                        "closures cannot be static"
-                    )
-                    .emit();
+                    self.tcx.sess.emit_err(ClosureCannotBeStatic { fn_decl_span });
                 }
                 None
             }
@@ -946,10 +916,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         fn_decl_span: Span,
     ) -> hir::ExprKind<'hir> {
         if let &ClosureBinder::For { span, .. } = binder {
-            self.tcx.sess.span_err(
-                span,
-                "`for<...>` binders on `async` closures are not currently supported",
-            );
+            self.tcx.sess.emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
         }
 
         let (binder_clause, generic_params) = self.lower_closure_binder(binder);
@@ -960,17 +927,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let body = self.with_new_scopes(|this| {
             // FIXME(cramertj): allow `async` non-`move` closures with arguments.
             if capture_clause == CaptureBy::Ref && !decl.inputs.is_empty() {
-                struct_span_err!(
-                    this.tcx.sess,
-                    fn_decl_span,
-                    E0708,
-                    "`async` non-`move` closures with parameters are not currently supported",
-                )
-                .help(
-                    "consider using `let` statements to manually capture \
-                    variables by reference before entering an `async move` closure",
-                )
-                .emit();
+                this.tcx.sess.emit_err(AsyncNonMoveClosureNotSupported { fn_decl_span });
             }
 
             // Transform `async |x: u8| -> X { ... }` into
@@ -1210,20 +1167,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 );
                 let fields_omitted = match &se.rest {
                     StructRest::Base(e) => {
-                        self.tcx
-                            .sess
-                            .struct_span_err(
-                                e.span,
-                                "functional record updates are not allowed in destructuring \
-                                    assignments",
-                            )
-                            .span_suggestion(
-                                e.span,
-                                "consider removing the trailing pattern",
-                                "",
-                                rustc_errors::Applicability::MachineApplicable,
-                            )
-                            .emit();
+                        self.tcx.sess.emit_err(FunctionalRecordUpdateDestructuringAssignemnt {
+                            span: e.span,
+                        });
                         true
                     }
                     StructRest::Rest(_) => true,
@@ -1420,13 +1366,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         match self.generator_kind {
             Some(hir::GeneratorKind::Gen) => {}
             Some(hir::GeneratorKind::Async(_)) => {
-                struct_span_err!(
-                    self.tcx.sess,
-                    span,
-                    E0727,
-                    "`async` generators are not yet supported"
-                )
-                .emit();
+                self.tcx.sess.emit_err(AsyncGeneratorsNotSupported { span });
             }
             None => self.generator_kind = Some(hir::GeneratorKind::Gen),
         }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 0f1bab24f96..fd338ffc0c5 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -1,3 +1,4 @@
+use super::errors::{InvalidAbi, MisplacedRelaxTraitBound};
 use super::ResolverAstLoweringExt;
 use super::{AstOwner, ImplTraitContext, ImplTraitPosition};
 use super::{FnDeclKind, LoweringContext, ParamMode};
@@ -7,7 +8,6 @@ use rustc_ast::visit::AssocCtxt;
 use rustc_ast::*;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
-use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -1260,10 +1260,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn error_on_invalid_abi(&self, abi: StrLit) {
-        struct_span_err!(self.tcx.sess, abi.span, E0703, "invalid ABI: found `{}`", abi.symbol)
-            .span_label(abi.span, "invalid ABI")
-            .help(&format!("valid ABIs: {}", abi::all_names().join(", ")))
-            .emit();
+        self.tcx.sess.emit_err(InvalidAbi {
+            span: abi.span,
+            abi: abi.symbol,
+            valid_abis: abi::all_names().join(", "),
+        });
     }
 
     fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
@@ -1338,11 +1339,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 }
                 let is_param = *is_param.get_or_insert_with(compute_is_param);
                 if !is_param {
-                    self.diagnostic().span_err(
-                        bound.span(),
-                        "`?Trait` bounds are only permitted at the \
-                        point where a type parameter is declared",
-                    );
+                    self.tcx.sess.emit_err(MisplacedRelaxTraitBound { span: bound.span() });
                 }
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 8ab1daf23e8..becb67d1165 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -39,6 +39,8 @@
 #[macro_use]
 extern crate tracing;
 
+use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
+
 use rustc_ast::ptr::P;
 use rustc_ast::visit;
 use rustc_ast::{self as ast, *};
@@ -49,7 +51,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{struct_span_err, Applicability, Handler, StashKey};
+use rustc_errors::{DiagnosticArgFromDisplay, Handler, StashKey};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID};
@@ -75,6 +77,7 @@ macro_rules! arena_vec {
 
 mod asm;
 mod block;
+mod errors;
 mod expr;
 mod index;
 mod item;
@@ -1070,19 +1073,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn emit_bad_parenthesized_trait_in_assoc_ty(&self, data: &ParenthesizedArgs) {
-        let mut err = self.tcx.sess.struct_span_err(
-            data.span,
-            "parenthesized generic arguments cannot be used in associated type constraints",
-        );
         // Suggest removing empty parentheses: "Trait()" -> "Trait"
-        if data.inputs.is_empty() {
+        let sub = if data.inputs.is_empty() {
             let parentheses_span =
                 data.inputs_span.shrink_to_lo().to(data.inputs_span.shrink_to_hi());
-            err.multipart_suggestion(
-                "remove these parentheses",
-                vec![(parentheses_span, String::new())],
-                Applicability::MaybeIncorrect,
-            );
+            AssocTyParenthesesSub::Empty { parentheses_span }
         }
         // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
         else {
@@ -1096,13 +1091,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // End of last argument to end of parameters
             let close_param =
                 data.inputs.last().unwrap().span.shrink_to_hi().to(data.inputs_span.shrink_to_hi());
-            err.multipart_suggestion(
-                &format!("use angle brackets instead",),
-                vec![(open_param, String::from("<")), (close_param, String::from(">"))],
-                Applicability::MaybeIncorrect,
-            );
-        }
-        err.emit();
+            AssocTyParenthesesSub::NotEmpty { open_param, close_param }
+        };
+        self.tcx.sess.emit_err(AssocTyParentheses { span: data.span, sub });
     }
 
     #[instrument(level = "debug", skip(self))]
@@ -1341,14 +1332,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                         path
                     }
                     ImplTraitContext::Disallowed(position) => {
-                        let mut err = struct_span_err!(
-                            self.tcx.sess,
-                            t.span,
-                            E0562,
-                            "`impl Trait` only allowed in function and inherent method return types, not in {}",
-                            position
-                        );
-                        err.emit();
+                        self.tcx.sess.emit_err(MisplacedImplTrait {
+                            span: t.span,
+                            position: DiagnosticArgFromDisplay(&position),
+                        });
                         hir::TyKind::Err
                     }
                 }
diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs
index 51f67e505f4..1efa19a3a82 100644
--- a/compiler/rustc_ast_lowering/src/pat.rs
+++ b/compiler/rustc_ast_lowering/src/pat.rs
@@ -1,3 +1,6 @@
+use super::errors::{
+    ArbitraryExpressionInPattern, ExtraDoubleDot, MisplacedDoubleDot, SubTupleBinding,
+};
 use super::ResolverAstLoweringExt;
 use super::{ImplTraitContext, LoweringContext, ParamMode};
 use crate::ImplTraitPosition;
@@ -5,7 +8,6 @@ use crate::ImplTraitPosition;
 use rustc_ast::ptr::P;
 use rustc_ast::*;
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::Res;
 use rustc_span::symbol::Ident;
@@ -134,20 +136,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // This is not allowed as a sub-tuple pattern
                 PatKind::Ident(ref _bm, ident, Some(ref sub)) if sub.is_rest() => {
                     let sp = pat.span;
-                    self.diagnostic()
-                        .struct_span_err(
-                            sp,
-                            &format!("`{} @` is not allowed in a {}", ident.name, ctx),
-                        )
-                        .span_label(sp, "this is only allowed in slice patterns")
-                        .help("remove this and bind each tuple field independently")
-                        .span_suggestion_verbose(
-                            sp,
-                            &format!("if you don't need to use the contents of {}, discard the tuple's remaining fields", ident),
-                            "..",
-                            Applicability::MaybeIncorrect,
-                        )
-                        .emit();
+                    self.tcx.sess.emit_err(SubTupleBinding {
+                        span: sp,
+                        ident_name: ident.name,
+                        ident,
+                        ctx,
+                    });
                 }
                 _ => {}
             }
@@ -296,19 +290,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
     /// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
     pub(crate) fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
-        self.diagnostic()
-            .struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
-            .span_label(sp, &format!("can only be used once per {} pattern", ctx))
-            .span_label(prev_sp, "previously used here")
-            .emit();
+        self.tcx.sess.emit_err(ExtraDoubleDot { span: sp, prev_span: prev_sp, ctx });
     }
 
     /// Used to ban the `..` pattern in places it shouldn't be semantically.
     fn ban_illegal_rest_pat(&self, sp: Span) -> hir::PatKind<'hir> {
-        self.diagnostic()
-            .struct_span_err(sp, "`..` patterns are not allowed here")
-            .note("only allowed in tuple, tuple struct, and slice patterns")
-            .emit();
+        self.tcx.sess.emit_err(MisplacedDoubleDot { span: sp });
 
         // We're not in a list context so `..` can be reasonably treated
         // as `_` because it should always be valid and roughly matches the
@@ -345,8 +332,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             ExprKind::Path(..) if allow_paths => {}
             ExprKind::Unary(UnOp::Neg, ref inner) if matches!(inner.kind, ExprKind::Lit(_)) => {}
             _ => {
-                self.diagnostic()
-                    .span_err(expr.span, "arbitrary expressions aren't allowed in patterns");
+                self.tcx.sess.emit_err(ArbitraryExpressionInPattern { span: expr.span });
                 return self.arena.alloc(self.expr_err(expr.span));
             }
         }
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index 393be3b454c..5874d08a94f 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -1,11 +1,11 @@
 use crate::ImplTraitPosition;
 
+use super::errors::{GenericTypeWithParentheses, UseAngleBrackets};
 use super::ResolverAstLoweringExt;
 use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
 use super::{ImplTraitContext, LoweringContext, ParamMode};
 
 use rustc_ast::{self as ast, *};
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, PartialRes, Res};
 use rustc_hir::GenericArg;
@@ -185,7 +185,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     ) -> hir::PathSegment<'hir> {
         debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
         let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
-            let msg = "parenthesized type parameters may only be used with a `Fn` trait";
             match **generic_args {
                 GenericArgs::AngleBracketed(ref data) => {
                     self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
@@ -193,10 +192,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
                     ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
                     ParenthesizedGenericArgs::Err => {
-                        let mut err = struct_span_err!(self.tcx.sess, data.span, E0214, "{}", msg);
-                        err.span_label(data.span, "only `Fn` traits may use parentheses");
                         // Suggest replacing parentheses with angle brackets `Trait(params...)` to `Trait<params...>`
-                        if !data.inputs.is_empty() {
+                        let sub = if !data.inputs.is_empty() {
                             // Start of the span to the 1st character of 1st argument
                             let open_param = data.inputs_span.shrink_to_lo().to(data
                                 .inputs
@@ -212,16 +209,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                                 .span
                                 .shrink_to_hi()
                                 .to(data.inputs_span.shrink_to_hi());
-                            err.multipart_suggestion(
-                                &format!("use angle brackets instead",),
-                                vec![
-                                    (open_param, String::from("<")),
-                                    (close_param, String::from(">")),
-                                ],
-                                Applicability::MaybeIncorrect,
-                            );
-                        }
-                        err.emit();
+
+                            Some(UseAngleBrackets { open_param, close_param })
+                        } else {
+                            None
+                        };
+                        self.tcx.sess.emit_err(GenericTypeWithParentheses { span: data.span, sub });
                         (
                             self.lower_angle_bracketed_parameter_data(
                                 &data.as_angle_bracketed_args(),
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 62ccd734fe7..65edab78ce7 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -3,7 +3,6 @@
 use rustc_ast as ast;
 use rustc_ast::{Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem, NodeId};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability};
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 use rustc_macros::HashStable_Generic;
 use rustc_session::lint::builtin::UNEXPECTED_CFGS;
@@ -14,6 +13,8 @@ use rustc_span::hygiene::Transparency;
 use rustc_span::{symbol::sym, symbol::Symbol, Span};
 use std::num::NonZeroU32;
 
+use crate::session_diagnostics::{self, IncorrectReprFormatGenericCause};
+
 pub fn is_builtin_attr(attr: &Attribute) -> bool {
     attr.is_doc_comment() || attr.ident().filter(|ident| is_builtin_attr_name(ident.name)).is_some()
 }
@@ -25,46 +26,38 @@ enum AttrError {
     NonIdentFeature,
     MissingFeature,
     MultipleStabilityLevels,
-    UnsupportedLiteral(&'static str, /* is_bytestr */ bool),
+    UnsupportedLiteral(UnsupportedLiteralReason, /* is_bytestr */ bool),
+}
+
+pub(crate) enum UnsupportedLiteralReason {
+    Generic,
+    CfgString,
+    DeprecatedString,
+    DeprecatedKvPair,
 }
 
 fn handle_errors(sess: &ParseSess, span: Span, error: AttrError) {
-    let diag = &sess.span_diagnostic;
     match error {
         AttrError::MultipleItem(item) => {
-            struct_span_err!(diag, span, E0538, "multiple '{}' items", item).emit();
+            sess.emit_err(session_diagnostics::MultipleItem { span, item });
         }
         AttrError::UnknownMetaItem(item, expected) => {
-            let expected = expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
-            struct_span_err!(diag, span, E0541, "unknown meta item '{}'", item)
-                .span_label(span, format!("expected one of {}", expected.join(", ")))
-                .emit();
+            sess.emit_err(session_diagnostics::UnknownMetaItem { span, item, expected });
         }
         AttrError::MissingSince => {
-            struct_span_err!(diag, span, E0542, "missing 'since'").emit();
+            sess.emit_err(session_diagnostics::MissingSince { span });
         }
         AttrError::NonIdentFeature => {
-            struct_span_err!(diag, span, E0546, "'feature' is not an identifier").emit();
+            sess.emit_err(session_diagnostics::NonIdentFeature { span });
         }
         AttrError::MissingFeature => {
-            struct_span_err!(diag, span, E0546, "missing 'feature'").emit();
+            sess.emit_err(session_diagnostics::MissingFeature { span });
         }
         AttrError::MultipleStabilityLevels => {
-            struct_span_err!(diag, span, E0544, "multiple stability levels").emit();
+            sess.emit_err(session_diagnostics::MultipleStabilityLevels { span });
         }
-        AttrError::UnsupportedLiteral(msg, is_bytestr) => {
-            let mut err = struct_span_err!(diag, span, E0565, "{}", msg);
-            if is_bytestr {
-                if let Ok(lint_str) = sess.source_map().span_to_snippet(span) {
-                    err.span_suggestion(
-                        span,
-                        "consider removing the prefix",
-                        &lint_str[1..],
-                        Applicability::MaybeIncorrect,
-                    );
-                }
-            }
-            err.emit();
+        AttrError::UnsupportedLiteral(reason, is_bytestr) => {
+            sess.emit_err(session_diagnostics::UnsupportedLiteral { span, reason, is_bytestr });
         }
     }
 }
@@ -243,8 +236,6 @@ where
     let mut promotable = false;
     let mut allowed_through_unstable_modules = false;
 
-    let diagnostic = &sess.parse_sess.span_diagnostic;
-
     'outer: for attr in attrs_iter {
         if ![
             sym::rustc_const_unstable,
@@ -284,7 +275,7 @@ where
                     *item = Some(v);
                     true
                 } else {
-                    struct_span_err!(diagnostic, meta.span, E0539, "incorrect meta item").emit();
+                    sess.emit_err(session_diagnostics::IncorrectMetaItem { span: meta.span });
                     false
                 }
             };
@@ -326,7 +317,7 @@ where
                             handle_errors(
                                 &sess.parse_sess,
                                 meta.span(),
-                                AttrError::UnsupportedLiteral("unsupported literal", false),
+                                AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
                             );
                             continue 'outer;
                         };
@@ -350,39 +341,28 @@ where
                                 // is a name/value pair string literal.
                                 issue_num = match issue.unwrap().as_str() {
                                     "none" => None,
-                                    issue => {
-                                        let emit_diag = |msg: &str| {
-                                            struct_span_err!(
-                                                diagnostic,
-                                                mi.span,
-                                                E0545,
-                                                "`issue` must be a non-zero numeric string \
-                                                or \"none\"",
-                                            )
-                                            .span_label(mi.name_value_literal_span().unwrap(), msg)
-                                            .emit();
-                                        };
-                                        match issue.parse() {
-                                            Ok(0) => {
-                                                emit_diag(
-                                                    "`issue` must not be \"0\", \
-                                                    use \"none\" instead",
-                                                );
-                                                continue 'outer;
-                                            }
-                                            Ok(num) => NonZeroU32::new(num),
-                                            Err(err) => {
-                                                emit_diag(&err.to_string());
-                                                continue 'outer;
-                                            }
+                                    issue => match issue.parse::<NonZeroU32>() {
+                                        Ok(num) => Some(num),
+                                        Err(err) => {
+                                            sess.emit_err(
+                                                session_diagnostics::InvalidIssueString {
+                                                    span: mi.span,
+                                                    cause: session_diagnostics::InvalidIssueStringCause::from_int_error_kind(
+                                                        mi.name_value_literal_span().unwrap(),
+                                                        err.kind(),
+                                                    ),
+                                                },
+                                            );
+                                            continue 'outer;
                                         }
-                                    }
+                                    },
                                 };
                             }
                             sym::soft => {
                                 if !mi.is_word() {
-                                    let msg = "`soft` should not have any arguments";
-                                    sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
+                                    sess.emit_err(session_diagnostics::SoftNoArgs {
+                                        span: mi.span,
+                                    });
                                 }
                                 is_soft = true;
                             }
@@ -440,8 +420,7 @@ where
                             continue;
                         }
                         _ => {
-                            struct_span_err!(diagnostic, attr.span, E0547, "missing 'issue'")
-                                .emit();
+                            sess.emit_err(session_diagnostics::MissingIssue { span: attr.span });
                             continue;
                         }
                     }
@@ -494,7 +473,10 @@ where
                                 handle_errors(
                                     &sess.parse_sess,
                                     lit.span,
-                                    AttrError::UnsupportedLiteral("unsupported literal", false),
+                                    AttrError::UnsupportedLiteral(
+                                        UnsupportedLiteralReason::Generic,
+                                        false,
+                                    ),
                                 );
                                 continue 'outer;
                             }
@@ -533,14 +515,7 @@ where
         if let Some((ref mut stab, _)) = const_stab {
             stab.promotable = promotable;
         } else {
-            struct_span_err!(
-                diagnostic,
-                item_sp,
-                E0717,
-                "`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` \
-                or a `rustc_const_stable` attribute"
-            )
-            .emit();
+            sess.emit_err(session_diagnostics::RustcPromotablePairing { span: item_sp });
         }
     }
 
@@ -555,13 +530,7 @@ where
         {
             *allowed_through_unstable_modules = true;
         } else {
-            struct_span_err!(
-                diagnostic,
-                item_sp,
-                E0789,
-                "`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute"
-            )
-            .emit();
+            sess.emit_err(session_diagnostics::RustcAllowedUnstablePairing { span: item_sp });
         }
     }
 
@@ -675,25 +644,18 @@ pub fn eval_condition(
                     NestedMetaItem::Literal(Lit { span, .. })
                     | NestedMetaItem::MetaItem(MetaItem { span, .. }),
                 ] => {
-                    sess.span_diagnostic
-                        .struct_span_err(*span, "expected a version literal")
-                        .emit();
+                    sess.emit_err(session_diagnostics::ExpectedVersionLiteral { span: *span });
                     return false;
                 }
                 [..] => {
-                    sess.span_diagnostic
-                        .struct_span_err(cfg.span, "expected single version literal")
-                        .emit();
+                    sess.emit_err(session_diagnostics::ExpectedSingleVersionLiteral {
+                        span: cfg.span,
+                    });
                     return false;
                 }
             };
             let Some(min_version) = parse_version(min_version.as_str(), false) else {
-                sess.span_diagnostic
-                    .struct_span_warn(
-                        *span,
-                        "unknown version literal format, assuming it refers to a future version",
-                    )
-                    .emit();
+                sess.emit_warning(session_diagnostics::UnknownVersionLiteral { span: *span });
                 return false;
             };
             let rustc_version = parse_version(env!("CFG_RELEASE"), true).unwrap();
@@ -711,7 +673,7 @@ pub fn eval_condition(
                     handle_errors(
                         sess,
                         mi.span(),
-                        AttrError::UnsupportedLiteral("unsupported literal", false),
+                        AttrError::UnsupportedLiteral(UnsupportedLiteralReason::Generic, false),
                     );
                     return false;
                 }
@@ -736,13 +698,9 @@ pub fn eval_condition(
                     }),
                 sym::not => {
                     if mis.len() != 1 {
-                        struct_span_err!(
-                            sess.span_diagnostic,
-                            cfg.span,
-                            E0536,
-                            "expected 1 cfg-pattern"
-                        )
-                        .emit();
+                        sess.emit_err(session_diagnostics::ExpectedOneCfgPattern {
+                            span: cfg.span,
+                        });
                         return false;
                     }
 
@@ -768,21 +726,16 @@ pub fn eval_condition(
                     })
                 }
                 _ => {
-                    struct_span_err!(
-                        sess.span_diagnostic,
-                        cfg.span,
-                        E0537,
-                        "invalid predicate `{}`",
-                        pprust::path_to_string(&cfg.path)
-                    )
-                    .emit();
+                    sess.emit_err(session_diagnostics::InvalidPredicate {
+                        span: cfg.span,
+                        predicate: pprust::path_to_string(&cfg.path),
+                    });
                     false
                 }
             }
         }
         ast::MetaItemKind::Word | MetaItemKind::NameValue(..) if cfg.path.segments.len() != 1 => {
-            sess.span_diagnostic
-                .span_err(cfg.path.span, "`cfg` predicate key must be an identifier");
+            sess.emit_err(session_diagnostics::CfgPredicateIdentifier { span: cfg.path.span });
             true
         }
         MetaItemKind::NameValue(ref lit) if !lit.kind.is_str() => {
@@ -790,7 +743,7 @@ pub fn eval_condition(
                 sess,
                 lit.span,
                 AttrError::UnsupportedLiteral(
-                    "literal in `cfg` predicate value must be a string",
+                    UnsupportedLiteralReason::CfgString,
                     lit.kind.is_bytestr(),
                 ),
             );
@@ -834,7 +787,6 @@ where
     I: Iterator<Item = &'a Attribute>,
 {
     let mut depr: Option<(Deprecation, Span)> = None;
-    let diagnostic = &sess.parse_sess.span_diagnostic;
     let is_rustc = sess.features_untracked().staged_api;
 
     'outer: for attr in attrs_iter {
@@ -870,14 +822,14 @@ where
                                 &sess.parse_sess,
                                 lit.span,
                                 AttrError::UnsupportedLiteral(
-                                    "literal in `deprecated` \
-                                    value must be a string",
+                                    UnsupportedLiteralReason::DeprecatedString,
                                     lit.kind.is_bytestr(),
                                 ),
                             );
                         } else {
-                            struct_span_err!(diagnostic, meta.span, E0551, "incorrect meta item")
-                                .emit();
+                            sess.emit_err(session_diagnostics::IncorrectMetaItem2 {
+                                span: meta.span,
+                            });
                         }
 
                         false
@@ -899,14 +851,11 @@ where
                             }
                             sym::suggestion => {
                                 if !sess.features_untracked().deprecated_suggestion {
-                                    let mut diag = sess.struct_span_err(
-                                        mi.span,
-                                        "suggestions on deprecated items are unstable",
-                                    );
-                                    if sess.is_nightly_build() {
-                                        diag.help("add `#![feature(deprecated_suggestion)]` to the crate root");
-                                    }
-                                    diag.note("see #94785 for more details").emit();
+                                    sess.emit_err(session_diagnostics::DeprecatedItemSuggestion {
+                                        span: mi.span,
+                                        is_nightly: sess.is_nightly_build().then_some(()),
+                                        details: (),
+                                    });
                                 }
 
                                 if !get(mi, &mut suggestion) {
@@ -934,7 +883,7 @@ where
                                 &sess.parse_sess,
                                 lit.span,
                                 AttrError::UnsupportedLiteral(
-                                    "item in `deprecated` must be a key/value pair",
+                                    UnsupportedLiteralReason::DeprecatedKvPair,
                                     false,
                                 ),
                             );
@@ -952,7 +901,7 @@ where
             }
 
             if note.is_none() {
-                struct_span_err!(diagnostic, attr.span, E0543, "missing 'note'").emit();
+                sess.emit_err(session_diagnostics::MissingNote { span: attr.span });
                 continue;
             }
         }
@@ -1022,19 +971,9 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                     sym::simd => Some(ReprSimd),
                     sym::transparent => Some(ReprTransparent),
                     sym::align => {
-                        let mut err = struct_span_err!(
-                            diagnostic,
-                            item.span(),
-                            E0589,
-                            "invalid `repr(align)` attribute: `align` needs an argument"
-                        );
-                        err.span_suggestion(
-                            item.span(),
-                            "supply an argument here",
-                            "align(...)",
-                            Applicability::HasPlaceholders,
-                        );
-                        err.emit();
+                        sess.emit_err(session_diagnostics::InvalidReprAlignNeedArg {
+                            span: item.span(),
+                        });
                         recognised = true;
                         None
                     }
@@ -1063,57 +1002,32 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                     || int_type_of_word(name).is_some()
                 {
                     recognised = true;
-                    struct_span_err!(
-                                diagnostic,
-                                item.span(),
-                                E0552,
-                                "invalid representation hint: `{}` does not take a parenthesized argument list",
-                                name.to_ident_string(),
-                            ).emit();
+                    sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
+                        span: item.span(),
+                        name: name.to_ident_string(),
+                    });
                 }
                 if let Some(literal_error) = literal_error {
-                    struct_span_err!(
-                        diagnostic,
-                        item.span(),
-                        E0589,
-                        "invalid `repr({})` attribute: {}",
-                        name.to_ident_string(),
-                        literal_error
-                    )
-                    .emit();
+                    sess.emit_err(session_diagnostics::InvalidReprGeneric {
+                        span: item.span(),
+                        repr_arg: name.to_ident_string(),
+                        error_part: literal_error,
+                    });
                 }
             } else if let Some(meta_item) = item.meta_item() {
                 if let MetaItemKind::NameValue(ref value) = meta_item.kind {
                     if meta_item.has_name(sym::align) || meta_item.has_name(sym::packed) {
                         let name = meta_item.name_or_empty().to_ident_string();
                         recognised = true;
-                        let mut err = struct_span_err!(
-                            diagnostic,
-                            item.span(),
-                            E0693,
-                            "incorrect `repr({})` attribute format",
-                            name,
-                        );
-                        match value.kind {
-                            ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
-                                err.span_suggestion(
-                                    item.span(),
-                                    "use parentheses instead",
-                                    format!("{}({})", name, int),
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                            ast::LitKind::Str(s, _) => {
-                                err.span_suggestion(
-                                    item.span(),
-                                    "use parentheses instead",
-                                    format!("{}({})", name, s),
-                                    Applicability::MachineApplicable,
-                                );
-                            }
-                            _ => {}
-                        }
-                        err.emit();
+                        sess.emit_err(session_diagnostics::IncorrectReprFormatGeneric {
+                            span: item.span(),
+                            repr_arg: &name,
+                            cause: IncorrectReprFormatGenericCause::from_lit_kind(
+                                item.span(),
+                                &value.kind,
+                                &name,
+                            ),
+                        });
                     } else {
                         if matches!(
                             meta_item.name_or_empty(),
@@ -1121,51 +1035,33 @@ pub fn parse_repr_attr(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
                         ) || int_type_of_word(meta_item.name_or_empty()).is_some()
                         {
                             recognised = true;
-                            struct_span_err!(
-                                diagnostic,
-                                meta_item.span,
-                                E0552,
-                                "invalid representation hint: `{}` does not take a value",
-                                meta_item.name_or_empty().to_ident_string(),
-                            )
-                            .emit();
+                            sess.emit_err(session_diagnostics::InvalidReprHintNoValue {
+                                span: meta_item.span,
+                                name: meta_item.name_or_empty().to_ident_string(),
+                            });
                         }
                     }
                 } else if let MetaItemKind::List(_) = meta_item.kind {
                     if meta_item.has_name(sym::align) {
                         recognised = true;
-                        struct_span_err!(
-                            diagnostic,
-                            meta_item.span,
-                            E0693,
-                            "incorrect `repr(align)` attribute format: \
-                                 `align` takes exactly one argument in parentheses"
-                        )
-                        .emit();
+                        sess.emit_err(session_diagnostics::IncorrectReprFormatAlignOneArg {
+                            span: meta_item.span,
+                        });
                     } else if meta_item.has_name(sym::packed) {
                         recognised = true;
-                        struct_span_err!(
-                            diagnostic,
-                            meta_item.span,
-                            E0552,
-                            "incorrect `repr(packed)` attribute format: \
-                                 `packed` takes exactly one parenthesized argument, \
-                                 or no parentheses at all"
-                        )
-                        .emit();
+                        sess.emit_err(session_diagnostics::IncorrectReprFormatPackedOneOrZeroArg {
+                            span: meta_item.span,
+                        });
                     } else if matches!(
                         meta_item.name_or_empty(),
                         sym::C | sym::simd | sym::transparent
                     ) || int_type_of_word(meta_item.name_or_empty()).is_some()
                     {
                         recognised = true;
-                        struct_span_err!(
-                                diagnostic,
-                                meta_item.span,
-                                E0552,
-                                "invalid representation hint: `{}` does not take a parenthesized argument list",
-                                meta_item.name_or_empty().to_ident_string(),
-                            ).emit();
+                        sess.emit_err(session_diagnostics::InvalidReprHintNoParen {
+                            span: meta_item.span,
+                            name: meta_item.name_or_empty().to_ident_string(),
+                        });
                     }
                 }
             }
@@ -1262,10 +1158,10 @@ fn allow_unstable<'a>(
     let list = attrs
         .filter_map(move |attr| {
             attr.meta_item_list().or_else(|| {
-                sess.diagnostic().span_err(
-                    attr.span,
-                    &format!("`{}` expects a list of feature names", symbol.to_ident_string()),
-                );
+                sess.emit_err(session_diagnostics::ExpectsFeatureList {
+                    span: attr.span,
+                    name: symbol.to_ident_string(),
+                });
                 None
             })
         })
@@ -1274,10 +1170,10 @@ fn allow_unstable<'a>(
     list.into_iter().filter_map(move |it| {
         let name = it.ident().map(|ident| ident.name);
         if name.is_none() {
-            sess.diagnostic().span_err(
-                it.span(),
-                &format!("`{}` expects feature names", symbol.to_ident_string()),
-            );
+            sess.emit_err(session_diagnostics::ExpectsFeatures {
+                span: it.span(),
+                name: symbol.to_ident_string(),
+            });
         }
         name
     })
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index c95c1c40a34..3a43f1aad02 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -5,11 +5,14 @@
 //! to this crate.
 
 #![feature(let_else)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_macros;
 
 mod builtin;
+mod session_diagnostics;
 
 pub use builtin::*;
 pub use IntType::*;
diff --git a/compiler/rustc_attr/src/session_diagnostics.rs b/compiler/rustc_attr/src/session_diagnostics.rs
new file mode 100644
index 00000000000..a75e7409fba
--- /dev/null
+++ b/compiler/rustc_attr/src/session_diagnostics.rs
@@ -0,0 +1,397 @@
+use std::num::IntErrorKind;
+
+use rustc_ast as ast;
+use rustc_errors::{error_code, fluent, Applicability, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_macros::SessionDiagnostic;
+use rustc_session::{parse::ParseSess, SessionDiagnostic};
+use rustc_span::{Span, Symbol};
+
+use crate::UnsupportedLiteralReason;
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::expected_one_cfg_pattern, code = "E0536")]
+pub(crate) struct ExpectedOneCfgPattern {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_predicate, code = "E0537")]
+pub(crate) struct InvalidPredicate {
+    #[primary_span]
+    pub span: Span,
+
+    pub predicate: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::multiple_item, code = "E0538")]
+pub(crate) struct MultipleItem {
+    #[primary_span]
+    pub span: Span,
+
+    pub item: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::incorrect_meta_item, code = "E0539")]
+pub(crate) struct IncorrectMetaItem {
+    #[primary_span]
+    pub span: Span,
+}
+
+// Error code: E0541
+pub(crate) struct UnknownMetaItem<'a> {
+    pub span: Span,
+    pub item: String,
+    pub expected: &'a [&'a str],
+}
+
+// Manual implementation to be able to format `expected` items correctly.
+impl<'a> SessionDiagnostic<'a> for UnknownMetaItem<'_> {
+    fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let expected = self.expected.iter().map(|name| format!("`{}`", name)).collect::<Vec<_>>();
+        let mut diag = sess.span_diagnostic.struct_span_err_with_code(
+            self.span,
+            fluent::attr::unknown_meta_item,
+            error_code!(E0541),
+        );
+        diag.set_arg("item", self.item);
+        diag.set_arg("expected", expected.join(", "));
+        diag.span_label(self.span, fluent::attr::label);
+        diag
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::missing_since, code = "E0542")]
+pub(crate) struct MissingSince {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::missing_note, code = "E0543")]
+pub(crate) struct MissingNote {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::multiple_stability_levels, code = "E0544")]
+pub(crate) struct MultipleStabilityLevels {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_issue_string, code = "E0545")]
+pub(crate) struct InvalidIssueString {
+    #[primary_span]
+    pub span: Span,
+
+    #[subdiagnostic]
+    pub cause: Option<InvalidIssueStringCause>,
+}
+
+// The error kinds of `IntErrorKind` are duplicated here in order to allow the messages to be
+// translatable.
+#[derive(SessionSubdiagnostic)]
+pub(crate) enum InvalidIssueStringCause {
+    #[label(attr::must_not_be_zero)]
+    MustNotBeZero {
+        #[primary_span]
+        span: Span,
+    },
+
+    #[label(attr::empty)]
+    Empty {
+        #[primary_span]
+        span: Span,
+    },
+
+    #[label(attr::invalid_digit)]
+    InvalidDigit {
+        #[primary_span]
+        span: Span,
+    },
+
+    #[label(attr::pos_overflow)]
+    PosOverflow {
+        #[primary_span]
+        span: Span,
+    },
+
+    #[label(attr::neg_overflow)]
+    NegOverflow {
+        #[primary_span]
+        span: Span,
+    },
+}
+
+impl InvalidIssueStringCause {
+    pub fn from_int_error_kind(span: Span, kind: &IntErrorKind) -> Option<Self> {
+        match kind {
+            IntErrorKind::Empty => Some(Self::Empty { span }),
+            IntErrorKind::InvalidDigit => Some(Self::InvalidDigit { span }),
+            IntErrorKind::PosOverflow => Some(Self::PosOverflow { span }),
+            IntErrorKind::NegOverflow => Some(Self::NegOverflow { span }),
+            IntErrorKind::Zero => Some(Self::MustNotBeZero { span }),
+            _ => None,
+        }
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::missing_feature, code = "E0546")]
+pub(crate) struct MissingFeature {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::non_ident_feature, code = "E0546")]
+pub(crate) struct NonIdentFeature {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::missing_issue, code = "E0547")]
+pub(crate) struct MissingIssue {
+    #[primary_span]
+    pub span: Span,
+}
+
+// FIXME: This diagnostic is identical to `IncorrectMetaItem`, barring the error code. Consider
+// changing this to `IncorrectMetaItem`. See #51489.
+#[derive(SessionDiagnostic)]
+#[diag(attr::incorrect_meta_item, code = "E0551")]
+pub(crate) struct IncorrectMetaItem2 {
+    #[primary_span]
+    pub span: Span,
+}
+
+// FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`?
+// It is more similar to `IncorrectReprFormatGeneric`.
+#[derive(SessionDiagnostic)]
+#[diag(attr::incorrect_repr_format_packed_one_or_zero_arg, code = "E0552")]
+pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_repr_hint_no_paren, code = "E0552")]
+pub(crate) struct InvalidReprHintNoParen {
+    #[primary_span]
+    pub span: Span,
+
+    pub name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_repr_hint_no_value, code = "E0552")]
+pub(crate) struct InvalidReprHintNoValue {
+    #[primary_span]
+    pub span: Span,
+
+    pub name: String,
+}
+
+// Error code: E0565
+pub(crate) struct UnsupportedLiteral {
+    pub span: Span,
+    pub reason: UnsupportedLiteralReason,
+    pub is_bytestr: bool,
+}
+
+impl<'a> SessionDiagnostic<'a> for UnsupportedLiteral {
+    fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+        let mut diag = sess.span_diagnostic.struct_span_err_with_code(
+            self.span,
+            match self.reason {
+                UnsupportedLiteralReason::Generic => fluent::attr::unsupported_literal_generic,
+                UnsupportedLiteralReason::CfgString => fluent::attr::unsupported_literal_cfg_string,
+                UnsupportedLiteralReason::DeprecatedString => {
+                    fluent::attr::unsupported_literal_deprecated_string
+                }
+                UnsupportedLiteralReason::DeprecatedKvPair => {
+                    fluent::attr::unsupported_literal_deprecated_kv_pair
+                }
+            },
+            error_code!(E0565),
+        );
+        if self.is_bytestr {
+            if let Ok(lint_str) = sess.source_map().span_to_snippet(self.span) {
+                diag.span_suggestion(
+                    self.span,
+                    fluent::attr::unsupported_literal_suggestion,
+                    &lint_str[1..],
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        }
+        diag
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_repr_align_need_arg, code = "E0589")]
+pub(crate) struct InvalidReprAlignNeedArg {
+    #[primary_span]
+    #[suggestion(code = "align(...)", applicability = "has-placeholders")]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::invalid_repr_generic, code = "E0589")]
+pub(crate) struct InvalidReprGeneric<'a> {
+    #[primary_span]
+    pub span: Span,
+
+    pub repr_arg: String,
+    pub error_part: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::incorrect_repr_format_align_one_arg, code = "E0693")]
+pub(crate) struct IncorrectReprFormatAlignOneArg {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::incorrect_repr_format_generic, code = "E0693")]
+pub(crate) struct IncorrectReprFormatGeneric<'a> {
+    #[primary_span]
+    pub span: Span,
+
+    pub repr_arg: &'a str,
+
+    #[subdiagnostic]
+    pub cause: Option<IncorrectReprFormatGenericCause<'a>>,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub(crate) enum IncorrectReprFormatGenericCause<'a> {
+    #[suggestion(attr::suggestion, code = "{name}({int})", applicability = "machine-applicable")]
+    Int {
+        #[primary_span]
+        span: Span,
+
+        #[skip_arg]
+        name: &'a str,
+
+        #[skip_arg]
+        int: u128,
+    },
+
+    #[suggestion(
+        attr::suggestion,
+        code = "{name}({symbol})",
+        applicability = "machine-applicable"
+    )]
+    Symbol {
+        #[primary_span]
+        span: Span,
+
+        #[skip_arg]
+        name: &'a str,
+
+        #[skip_arg]
+        symbol: Symbol,
+    },
+}
+
+impl<'a> IncorrectReprFormatGenericCause<'a> {
+    pub fn from_lit_kind(span: Span, kind: &ast::LitKind, name: &'a str) -> Option<Self> {
+        match kind {
+            ast::LitKind::Int(int, ast::LitIntType::Unsuffixed) => {
+                Some(Self::Int { span, name, int: *int })
+            }
+            ast::LitKind::Str(symbol, _) => Some(Self::Symbol { span, name, symbol: *symbol }),
+            _ => None,
+        }
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::rustc_promotable_pairing, code = "E0717")]
+pub(crate) struct RustcPromotablePairing {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::rustc_allowed_unstable_pairing, code = "E0789")]
+pub(crate) struct RustcAllowedUnstablePairing {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::cfg_predicate_identifier)]
+pub(crate) struct CfgPredicateIdentifier {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::deprecated_item_suggestion)]
+pub(crate) struct DeprecatedItemSuggestion {
+    #[primary_span]
+    pub span: Span,
+
+    #[help]
+    pub is_nightly: Option<()>,
+
+    #[note]
+    pub details: (),
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::expected_single_version_literal)]
+pub(crate) struct ExpectedSingleVersionLiteral {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::expected_version_literal)]
+pub(crate) struct ExpectedVersionLiteral {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::expects_feature_list)]
+pub(crate) struct ExpectsFeatureList {
+    #[primary_span]
+    pub span: Span,
+
+    pub name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::expects_features)]
+pub(crate) struct ExpectsFeatures {
+    #[primary_span]
+    pub span: Span,
+
+    pub name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::soft_no_args)]
+pub(crate) struct SoftNoArgs {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(attr::unknown_version_literal)]
+pub(crate) struct UnknownVersionLiteral {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_codegen_gcc/patches/0024-core-Disable-portable-simd-test.patch b/compiler/rustc_codegen_gcc/patches/0024-core-Disable-portable-simd-test.patch
index d5fa1cec061..c59a40df039 100644
--- a/compiler/rustc_codegen_gcc/patches/0024-core-Disable-portable-simd-test.patch
+++ b/compiler/rustc_codegen_gcc/patches/0024-core-Disable-portable-simd-test.patch
@@ -14,7 +14,6 @@ index 06c7be0..359e2e7 100644
 @@ -75,7 +75,6 @@
  #![feature(never_type)]
  #![feature(unwrap_infallible)]
- #![feature(result_into_ok_or_err)]
 -#![feature(portable_simd)]
  #![feature(ptr_metadata)]
  #![feature(once_cell)]
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 1802eedf193..0faf51b062b 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -168,6 +168,13 @@ pub struct CodegenResults {
     pub crate_info: CrateInfo,
 }
 
+pub enum CodegenErrors<'a> {
+    WrongFileType,
+    EmptyVersionNumber,
+    EncodingVersionMismatch { version_array: String, rlink_version: u32 },
+    RustcVersionMismatch { rustc_version: String, current_version: &'a str },
+}
+
 pub fn provide(providers: &mut Providers) {
     crate::back::symbol_export::provide(providers);
     crate::base::provide(providers);
@@ -212,30 +219,34 @@ impl CodegenResults {
         encoder.finish()
     }
 
-    pub fn deserialize_rlink(data: Vec<u8>) -> Result<Self, String> {
+    pub fn deserialize_rlink<'a>(data: Vec<u8>) -> Result<Self, CodegenErrors<'a>> {
         // The Decodable machinery is not used here because it panics if the input data is invalid
         // and because its internal representation may change.
         if !data.starts_with(RLINK_MAGIC) {
-            return Err("The input does not look like a .rlink file".to_string());
+            return Err(CodegenErrors::WrongFileType);
         }
         let data = &data[RLINK_MAGIC.len()..];
         if data.len() < 4 {
-            return Err("The input does not contain version number".to_string());
+            return Err(CodegenErrors::EmptyVersionNumber);
         }
 
         let mut version_array: [u8; 4] = Default::default();
         version_array.copy_from_slice(&data[..4]);
         if u32::from_be_bytes(version_array) != RLINK_VERSION {
-            return Err(".rlink file was produced with encoding version {version_array}, but the current version is {RLINK_VERSION}".to_string());
+            return Err(CodegenErrors::EncodingVersionMismatch {
+                version_array: String::from_utf8_lossy(&version_array).to_string(),
+                rlink_version: RLINK_VERSION,
+            });
         }
 
         let mut decoder = MemDecoder::new(&data[4..], 0);
         let rustc_version = decoder.read_str();
         let current_version = RUSTC_VERSION.unwrap();
         if rustc_version != current_version {
-            return Err(format!(
-                ".rlink file was produced by rustc version {rustc_version}, but the current version is {current_version}."
-            ));
+            return Err(CodegenErrors::RustcVersionMismatch {
+                rustc_version: rustc_version.to_string(),
+                current_version,
+            });
         }
 
         let codegen_results = CodegenResults::decode(&mut decoder);
diff --git a/compiler/rustc_const_eval/src/const_eval/mod.rs b/compiler/rustc_const_eval/src/const_eval/mod.rs
index 948c3349498..d9c4ae4d53f 100644
--- a/compiler/rustc_const_eval/src/const_eval/mod.rs
+++ b/compiler/rustc_const_eval/src/const_eval/mod.rs
@@ -1,16 +1,16 @@
 // Not in interpret to make sure we do not use private implementation details
 
+use crate::errors::MaxNumNodesInConstErr;
+use crate::interpret::{
+    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
+    Scalar,
+};
 use rustc_hir::Mutability;
 use rustc_middle::mir;
 use rustc_middle::mir::interpret::{EvalToValTreeResult, GlobalId};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
 
-use crate::interpret::{
-    intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
-    Scalar,
-};
-
 mod error;
 mod eval_queries;
 mod fn_queries;
@@ -72,12 +72,17 @@ pub(crate) fn eval_to_valtree<'tcx>(
         Ok(valtree) => Ok(Some(valtree)),
         Err(err) => {
             let did = cid.instance.def_id();
-            let s = cid.display(tcx);
+            let global_const_id = cid.display(tcx);
             match err {
                 ValTreeCreationError::NodesOverflow => {
-                    let msg = format!("maximum number of nodes exceeded in constant {}", &s);
+                    let msg = format!(
+                        "maximum number of nodes exceeded in constant {}",
+                        &global_const_id
+                    );
                     let mut diag = match tcx.hir().span_if_local(did) {
-                        Some(span) => tcx.sess.struct_span_err(span, &msg),
+                        Some(span) => {
+                            tcx.sess.create_err(MaxNumNodesInConstErr { span, global_const_id })
+                        }
                         None => tcx.sess.struct_err(&msg),
                     };
                     diag.emit();
diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs
index 01619dee0e4..c3547cb3abd 100644
--- a/compiler/rustc_const_eval/src/errors.rs
+++ b/compiler/rustc_const_eval/src/errors.rs
@@ -87,3 +87,110 @@ pub(crate) struct TransientMutBorrowErrRaw {
     pub span: Span,
     pub kind: ConstContext,
 }
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::max_num_nodes_in_const)]
+pub(crate) struct MaxNumNodesInConstErr {
+    #[primary_span]
+    pub span: Span,
+    pub global_const_id: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_fn_pointer_call)]
+pub(crate) struct UnallowedFnPointerCall {
+    #[primary_span]
+    pub span: Span,
+    pub kind: ConstContext,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unstable_const_fn)]
+pub(crate) struct UnstableConstFn {
+    #[primary_span]
+    pub span: Span,
+    pub def_path: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_mutable_refs, code = "E0764")]
+pub(crate) struct UnallowedMutableRefs {
+    #[primary_span]
+    pub span: Span,
+    pub kind: ConstContext,
+    #[note(const_eval::teach_note)]
+    pub teach: Option<()>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_mutable_refs_raw, code = "E0764")]
+pub(crate) struct UnallowedMutableRefsRaw {
+    #[primary_span]
+    pub span: Span,
+    pub kind: ConstContext,
+    #[note(const_eval::teach_note)]
+    pub teach: Option<()>,
+}
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::non_const_fmt_macro_call, code = "E0015")]
+pub(crate) struct NonConstFmtMacroCall {
+    #[primary_span]
+    pub span: Span,
+    pub kind: ConstContext,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::non_const_fn_call, code = "E0015")]
+pub(crate) struct NonConstFnCall {
+    #[primary_span]
+    pub span: Span,
+    pub def_path_str: String,
+    pub kind: ConstContext,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_op_in_const_context)]
+pub(crate) struct UnallowedOpInConstContext {
+    #[primary_span]
+    pub span: Span,
+    pub msg: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_heap_allocations, code = "E0010")]
+pub(crate) struct UnallowedHeapAllocations {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    pub kind: ConstContext,
+    #[note(const_eval::teach_note)]
+    pub teach: Option<()>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::unallowed_inline_asm, code = "E0015")]
+pub(crate) struct UnallowedInlineAsm {
+    #[primary_span]
+    pub span: Span,
+    pub kind: ConstContext,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::interior_mutable_data_refer, code = "E0492")]
+pub(crate) struct InteriorMutableDataRefer {
+    #[primary_span]
+    #[label]
+    pub span: Span,
+    #[help]
+    pub opt_help: Option<()>,
+    pub kind: ConstContext,
+    #[note(const_eval::teach_note)]
+    pub teach: Option<()>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(const_eval::interior_mutability_borrow)]
+pub(crate) struct InteriorMutabilityBorrow {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
index c9cfc1f3f46..5fb4bf638b3 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -24,8 +24,11 @@ use rustc_trait_selection::traits::SelectionContext;
 
 use super::ConstCx;
 use crate::errors::{
-    MutDerefErr, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
-    TransientMutBorrowErr, TransientMutBorrowErrRaw,
+    InteriorMutabilityBorrow, InteriorMutableDataRefer, MutDerefErr, NonConstFmtMacroCall,
+    NonConstFnCall, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
+    TransientMutBorrowErr, TransientMutBorrowErrRaw, UnallowedFnPointerCall,
+    UnallowedHeapAllocations, UnallowedInlineAsm, UnallowedMutableRefs, UnallowedMutableRefsRaw,
+    UnallowedOpInConstContext, UnstableConstFn,
 };
 use crate::util::{call_kind, CallDesugaringKind, CallKind};
 
@@ -97,10 +100,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        ccx.tcx.sess.struct_span_err(
-            span,
-            &format!("function pointer calls are not allowed in {}s", ccx.const_kind()),
-        )
+        ccx.tcx.sess.create_err(UnallowedFnPointerCall { span, kind: ccx.const_kind() })
     }
 }
 
@@ -308,22 +308,13 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                 err
             }
             _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => {
-                struct_span_err!(
-                    ccx.tcx.sess,
-                    span,
-                    E0015,
-                    "cannot call non-const formatting macro in {}s",
-                    ccx.const_kind(),
-                )
+                ccx.tcx.sess.create_err(NonConstFmtMacroCall { span, kind: ccx.const_kind() })
             }
-            _ => struct_span_err!(
-                ccx.tcx.sess,
+            _ => ccx.tcx.sess.create_err(NonConstFnCall {
                 span,
-                E0015,
-                "cannot call non-const fn `{}` in {}s",
-                ccx.tcx.def_path_str_with_substs(callee, substs),
-                ccx.const_kind(),
-            ),
+                def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
+                kind: ccx.const_kind(),
+            }),
         };
 
         err.note(&format!(
@@ -354,10 +345,10 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
         let FnCallUnstable(def_id, feature) = *self;
 
-        let mut err = ccx.tcx.sess.struct_span_err(
-            span,
-            &format!("`{}` is not yet stable as a const fn", ccx.tcx.def_path_str(def_id)),
-        );
+        let mut err = ccx
+            .tcx
+            .sess
+            .create_err(UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
 
         if ccx.is_const_stable_const_fn() {
             err.help("const-stable functions can only call other const-stable functions");
@@ -392,9 +383,12 @@ impl<'tcx> NonConstOp<'tcx> for Generator {
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
         let msg = format!("{}s are not allowed in {}s", self.0, ccx.const_kind());
         if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
-            feature_err(&ccx.tcx.sess.parse_sess, sym::const_async_blocks, span, &msg)
+            ccx.tcx.sess.create_feature_err(
+                UnallowedOpInConstContext { span, msg },
+                sym::const_async_blocks,
+            )
         } else {
-            ccx.tcx.sess.struct_span_err(span, &msg)
+            ccx.tcx.sess.create_err(UnallowedOpInConstContext { span, msg })
         }
     }
 }
@@ -407,23 +401,11 @@ impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let mut err = struct_span_err!(
-            ccx.tcx.sess,
+        ccx.tcx.sess.create_err(UnallowedHeapAllocations {
             span,
-            E0010,
-            "allocations are not allowed in {}s",
-            ccx.const_kind()
-        );
-        err.span_label(span, format!("allocation not allowed in {}s", ccx.const_kind()));
-        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
-            err.note(
-                "The value of statics and constants must be known at compile time, \
-                 and they live for the entire lifetime of a program. Creating a boxed \
-                 value allocates memory on the heap at runtime, and therefore cannot \
-                 be done at compile time.",
-            );
-        }
-        err
+            kind: ccx.const_kind(),
+            teach: ccx.tcx.sess.teach(&error_code!(E0010)).then_some(()),
+        })
     }
 }
 
@@ -435,13 +417,7 @@ impl<'tcx> NonConstOp<'tcx> for InlineAsm {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        struct_span_err!(
-            ccx.tcx.sess,
-            span,
-            E0015,
-            "inline assembly is not allowed in {}s",
-            ccx.const_kind()
-        )
+        ccx.tcx.sess.create_err(UnallowedInlineAsm { span, kind: ccx.const_kind() })
     }
 }
 
@@ -487,12 +463,7 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        feature_err(
-            &ccx.tcx.sess.parse_sess,
-            sym::const_refs_to_cell,
-            span,
-            "cannot borrow here, since the borrowed element may contain interior mutability",
-        )
+        ccx.tcx.sess.create_feature_err(InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
     }
 }
 
@@ -507,32 +478,22 @@ impl<'tcx> NonConstOp<'tcx> for CellBorrow {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let mut err = struct_span_err!(
-            ccx.tcx.sess,
-            span,
-            E0492,
-            "{}s cannot refer to interior mutable data",
-            ccx.const_kind(),
-        );
-        err.span_label(
-            span,
-            "this borrow of an interior mutable value may end up in the final value",
-        );
+        // FIXME: Maybe a more elegant solution to this if else case
         if let hir::ConstContext::Static(_) = ccx.const_kind() {
-            err.help(
-                "to fix this, the value can be extracted to a separate \
-                `static` item and then referenced",
-            );
-        }
-        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
-            err.note(
-                "A constant containing interior mutable data behind a reference can allow you
-                 to modify that data. This would make multiple uses of a constant to be able to
-                 see different values and allow circumventing the `Send` and `Sync` requirements
-                 for shared mutable data, which is unsound.",
-            );
+            ccx.tcx.sess.create_err(InteriorMutableDataRefer {
+                span,
+                opt_help: Some(()),
+                kind: ccx.const_kind(),
+                teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
+            })
+        } else {
+            ccx.tcx.sess.create_err(InteriorMutableDataRefer {
+                span,
+                opt_help: None,
+                kind: ccx.const_kind(),
+                teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
+            })
         }
-        err
     }
 }
 
@@ -558,33 +519,18 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
         ccx: &ConstCx<'_, 'tcx>,
         span: Span,
     ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
-        let raw = match self.0 {
-            hir::BorrowKind::Raw => "raw ",
-            hir::BorrowKind::Ref => "",
-        };
-
-        let mut err = struct_span_err!(
-            ccx.tcx.sess,
-            span,
-            E0764,
-            "{}mutable references are not allowed in the final value of {}s",
-            raw,
-            ccx.const_kind(),
-        );
-
-        if ccx.tcx.sess.teach(&err.get_code().unwrap()) {
-            err.note(
-                "References in statics and constants may only refer \
-                      to immutable values.\n\n\
-                      Statics are shared everywhere, and if they refer to \
-                      mutable data one might violate memory safety since \
-                      holding multiple mutable references to shared data \
-                      is not allowed.\n\n\
-                      If you really want global mutable state, try using \
-                      static mut or a global UnsafeCell.",
-            );
+        match self.0 {
+            hir::BorrowKind::Raw => ccx.tcx.sess.create_err(UnallowedMutableRefsRaw {
+                span,
+                kind: ccx.const_kind(),
+                teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
+            }),
+            hir::BorrowKind::Ref => ccx.tcx.sess.create_err(UnallowedMutableRefs {
+                span,
+                kind: ccx.const_kind(),
+                teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
+            }),
         }
-        err
     }
 }
 
diff --git a/compiler/rustc_driver/Cargo.toml b/compiler/rustc_driver/Cargo.toml
index 08d5d4f343c..4570c144833 100644
--- a/compiler/rustc_driver/Cargo.toml
+++ b/compiler/rustc_driver/Cargo.toml
@@ -19,6 +19,7 @@ rustc_errors = { path = "../rustc_errors" }
 rustc_feature = { path = "../rustc_feature" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_hir_pretty = { path = "../rustc_hir_pretty" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_metadata = { path = "../rustc_metadata" }
 rustc_parse = { path = "../rustc_parse" }
 rustc_plugin_impl = { path = "../rustc_plugin_impl" }
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index dac8df7dc55..a193d5db691 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -9,6 +9,8 @@
 #![feature(once_cell)]
 #![recursion_limit = "256"]
 #![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate tracing;
@@ -16,7 +18,7 @@ extern crate tracing;
 pub extern crate rustc_plugin_impl as plugin;
 
 use rustc_ast as ast;
-use rustc_codegen_ssa::{traits::CodegenBackend, CodegenResults};
+use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
 use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
@@ -56,6 +58,12 @@ use std::time::Instant;
 
 pub mod args;
 pub mod pretty;
+mod session_diagnostics;
+
+use crate::session_diagnostics::{
+    RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
+    RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
+};
 
 /// Exit status code used for successful compilation and help output.
 pub const EXIT_SUCCESS: i32 = 0;
@@ -581,18 +589,35 @@ pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Comp
             sess.init_crate_types(collect_crate_types(sess, &[]));
             let outputs = compiler.build_output_filenames(sess, &[]);
             let rlink_data = fs::read(file).unwrap_or_else(|err| {
-                sess.fatal(&format!("failed to read rlink file: {}", err));
+                sess.emit_fatal(RlinkUnableToRead { err });
             });
             let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
                 Ok(codegen) => codegen,
-                Err(error) => {
-                    sess.fatal(&format!("Could not deserialize .rlink file: {error}"));
+                Err(err) => {
+                    match err {
+                        CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType),
+                        CodegenErrors::EmptyVersionNumber => {
+                            sess.emit_fatal(RLinkEmptyVersionNumber)
+                        }
+                        CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => {
+                            sess.emit_fatal(RLinkEncodingVersionMismatch {
+                                version_array,
+                                rlink_version,
+                            })
+                        }
+                        CodegenErrors::RustcVersionMismatch { rustc_version, current_version } => {
+                            sess.emit_fatal(RLinkRustcVersionMismatch {
+                                rustc_version,
+                                current_version,
+                            })
+                        }
+                    };
                 }
             };
             let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
             abort_on_err(result, sess);
         } else {
-            sess.fatal("rlink must be a file")
+            sess.emit_fatal(RlinkNotAFile {})
         }
         Compilation::Stop
     } else {
diff --git a/compiler/rustc_driver/src/session_diagnostics.rs b/compiler/rustc_driver/src/session_diagnostics.rs
new file mode 100644
index 00000000000..fe64d0fca9b
--- /dev/null
+++ b/compiler/rustc_driver/src/session_diagnostics.rs
@@ -0,0 +1,33 @@
+use rustc_macros::SessionDiagnostic;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_unable_to_read)]
+pub(crate) struct RlinkUnableToRead {
+    pub err: std::io::Error,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_wrong_file_type)]
+pub(crate) struct RLinkWrongFileType;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_empty_version_number)]
+pub(crate) struct RLinkEmptyVersionNumber;
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_encoding_version_mismatch)]
+pub(crate) struct RLinkEncodingVersionMismatch {
+    pub version_array: String,
+    pub rlink_version: u32,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_rustc_version_mismatch)]
+pub(crate) struct RLinkRustcVersionMismatch<'a> {
+    pub rustc_version: String,
+    pub current_version: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(driver::rlink_no_a_file)]
+pub(crate) struct RlinkNotAFile;
diff --git a/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
new file mode 100644
index 00000000000..dcb1e2b0830
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/ast_lowering.ftl
@@ -0,0 +1,131 @@
+ast_lowering_generic_type_with_parentheses =
+    parenthesized type parameters may only be used with a `Fn` trait
+    .label = only `Fn` traits may use parentheses
+
+ast_lowering_use_angle_brackets = use angle brackets instead
+
+ast_lowering_invalid_abi =
+    invalid ABI: found `{$abi}`
+    .label = invalid ABI
+    .help = valid ABIs: {$valid_abis}
+
+ast_lowering_assoc_ty_parentheses =
+    parenthesized generic arguments cannot be used in associated type constraints
+
+ast_lowering_remove_parentheses = remove these parentheses
+
+ast_lowering_misplaced_impl_trait =
+    `impl Trait` only allowed in function and inherent method return types, not in {$position}
+
+ast_lowering_rustc_box_attribute_error =
+    #[rustc_box] requires precisely one argument and no other attributes are allowed
+
+ast_lowering_underscore_expr_lhs_assign =
+    in expressions, `_` can only be used on the left-hand side of an assignment
+    .label = `_` not allowed here
+
+ast_lowering_base_expression_double_dot =
+    base expression required after `..`
+    .label = add a base expression here
+
+ast_lowering_await_only_in_async_fn_and_blocks =
+    `await` is only allowed inside `async` functions and blocks
+    .label = only allowed inside `async` functions and blocks
+
+ast_lowering_this_not_async = this is not `async`
+
+ast_lowering_generator_too_many_parameters =
+    too many parameters for a generator (expected 0 or 1 parameters)
+
+ast_lowering_closure_cannot_be_static = closures cannot be static
+
+ast_lowering_async_non_move_closure_not_supported =
+    `async` non-`move` closures with parameters are not currently supported
+    .help = consider using `let` statements to manually capture variables by reference before entering an `async move` closure
+
+ast_lowering_functional_record_update_destructuring_assignment =
+    functional record updates are not allowed in destructuring assignments
+    .suggestion = consider removing the trailing pattern
+
+ast_lowering_async_generators_not_supported =
+    `async` generators are not yet supported
+
+ast_lowering_inline_asm_unsupported_target =
+    inline assembly is unsupported on this target
+
+ast_lowering_att_syntax_only_x86 =
+    the `att_syntax` option is only supported on x86
+
+ast_lowering_abi_specified_multiple_times =
+    `{$prev_name}` ABI specified multiple times
+    .label = previously specified here
+    .note = these ABIs are equivalent on the current target
+
+ast_lowering_clobber_abi_not_supported =
+    `clobber_abi` is not supported on this target
+
+ast_lowering_invalid_abi_clobber_abi =
+    invalid ABI for `clobber_abi`
+    .note = the following ABIs are supported on this target: {$supported_abis}
+
+ast_lowering_invalid_register =
+    invalid register `{$reg}`: {$error}
+
+ast_lowering_invalid_register_class =
+    invalid register class `{$reg_class}`: {$error}
+
+ast_lowering_invalid_asm_template_modifier_reg_class =
+    invalid asm template modifier for this register class
+
+ast_lowering_argument = argument
+
+ast_lowering_template_modifier = template modifier
+
+ast_lowering_support_modifiers =
+    the `{$class_name}` register class supports the following template modifiers: {$modifiers}
+
+ast_lowering_does_not_support_modifiers =
+    the `{$class_name}` register class does not support template modifiers
+
+ast_lowering_invalid_asm_template_modifier_const =
+    asm template modifiers are not allowed for `const` arguments
+
+ast_lowering_invalid_asm_template_modifier_sym =
+    asm template modifiers are not allowed for `sym` arguments
+
+ast_lowering_register_class_only_clobber =
+    register class `{$reg_class_name}` can only be used as a clobber, not as an input or output
+
+ast_lowering_register_conflict =
+    register `{$reg1_name}` conflicts with register `{$reg2_name}`
+    .help = use `lateout` instead of `out` to avoid conflict
+
+ast_lowering_register1 = register `{$reg1_name}`
+
+ast_lowering_register2 = register `{$reg2_name}`
+
+ast_lowering_sub_tuple_binding =
+    `{$ident_name} @` is not allowed in a {$ctx}
+    .label = this is only allowed in slice patterns
+    .help = remove this and bind each tuple field independently
+
+ast_lowering_sub_tuple_binding_suggestion = if you don't need to use the contents of {$ident}, discard the tuple's remaining fields
+
+ast_lowering_extra_double_dot =
+    `..` can only be used once per {$ctx} pattern
+    .label = can only be used once per {$ctx} pattern
+
+ast_lowering_previously_used_here = previously used here
+
+ast_lowering_misplaced_double_dot =
+    `..` patterns are not allowed here
+    .note = only allowed in tuple, tuple struct, and slice patterns
+
+ast_lowering_misplaced_relax_trait_bound =
+    `?Trait` bounds are only permitted at the point where a type parameter is declared
+
+ast_lowering_not_supported_for_lifetime_binder_async_closure =
+    `for<...>` binders on `async` closures are not currently supported
+
+ast_lowering_arbitrary_expression_in_pattern =
+    arbitrary expressions aren't allowed in patterns
diff --git a/compiler/rustc_error_messages/locales/en-US/attr.ftl b/compiler/rustc_error_messages/locales/en-US/attr.ftl
new file mode 100644
index 00000000000..a7f8c993d42
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/attr.ftl
@@ -0,0 +1,107 @@
+attr_expected_one_cfg_pattern =
+    expected 1 cfg-pattern
+
+attr_invalid_predicate =
+    invalid predicate `{$predicate}`
+
+attr_multiple_item =
+    multiple '{$item}' items
+
+attr_incorrect_meta_item =
+    incorrect meta item
+
+attr_unknown_meta_item =
+    unknown meta item '{$item}'
+    .label = expected one of {$expected}
+
+attr_missing_since =
+    missing 'since'
+
+attr_missing_note =
+    missing 'note'
+
+attr_multiple_stability_levels =
+    multiple stability levels
+
+attr_invalid_issue_string =
+    `issue` must be a non-zero numeric string or "none"
+    .must_not_be_zero = `issue` must not be "0", use "none" instead
+    .empty = cannot parse integer from empty string
+    .invalid_digit = invalid digit found in string
+    .pos_overflow = number too large to fit in target type
+    .neg_overflow = number too small to fit in target type
+
+attr_missing_feature =
+    missing 'feature'
+
+attr_non_ident_feature =
+    'feature' is not an identifier
+
+attr_missing_issue =
+    missing 'issue'
+
+attr_incorrect_repr_format_packed_one_or_zero_arg =
+    incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all
+
+attr_invalid_repr_hint_no_paren =
+    invalid representation hint: `{$name}` does not take a parenthesized argument list
+
+attr_invalid_repr_hint_no_value =
+    invalid representation hint: `{$name}` does not take a value
+
+attr_unsupported_literal_generic =
+    unsupported literal
+attr_unsupported_literal_cfg_string =
+    literal in `cfg` predicate value must be a string
+attr_unsupported_literal_deprecated_string =
+    literal in `deprecated` value must be a string
+attr_unsupported_literal_deprecated_kv_pair =
+    item in `deprecated` must be a key/value pair
+attr_unsupported_literal_suggestion =
+    consider removing the prefix
+
+attr_invalid_repr_align_need_arg =
+    invalid `repr(align)` attribute: `align` needs an argument
+    .suggestion = supply an argument here
+
+attr_invalid_repr_generic =
+    invalid `repr({$repr_arg})` attribute: {$error_part}
+
+attr_incorrect_repr_format_align_one_arg =
+    incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses
+
+attr_incorrect_repr_format_generic =
+    incorrect `repr({$repr_arg})` attribute format
+    .suggestion = use parentheses instead
+
+attr_rustc_promotable_pairing =
+    `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute
+
+attr_rustc_allowed_unstable_pairing =
+    `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute
+
+attr_cfg_predicate_identifier =
+    `cfg` predicate key must be an identifier
+
+attr_deprecated_item_suggestion =
+    suggestions on deprecated items are unstable
+    .help = add `#![feature(deprecated_suggestion)]` to the crate root
+    .note = see #94785 for more details
+
+attr_expected_single_version_literal =
+    expected single version literal
+
+attr_expected_version_literal =
+    expected a version literal
+
+attr_expects_feature_list =
+    `{$name}` expects a list of feature names
+
+attr_expects_features =
+    `{$name}` expects feature names
+
+attr_soft_no_args =
+    `soft` should not have any arguments
+
+attr_unknown_version_literal =
+    unknown version literal format, assuming it refers to a future version
diff --git a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl
index 341f05efefd..33bb116d6fa 100644
--- a/compiler/rustc_error_messages/locales/en-US/const_eval.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/const_eval.ftl
@@ -29,3 +29,55 @@ const_eval_mut_deref =
 const_eval_transient_mut_borrow = mutable references are not allowed in {$kind}s
 
 const_eval_transient_mut_borrow_raw = raw mutable references are not allowed in {$kind}s
+
+const_eval_max_num_nodes_in_const = maximum number of nodes exceeded in constant {$global_const_id}
+
+const_eval_unallowed_fn_pointer_call = function pointer calls are not allowed in {$kind}s
+
+const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn
+
+const_eval_unallowed_mutable_refs =
+    mutable references are not allowed in the final value of {$kind}s
+    .teach_note =
+        References in statics and constants may only refer to immutable values.\n\n
+        Statics are shared everywhere, and if they refer to mutable data one might violate memory
+        safety since holding multiple mutable references to shared data is not allowed.\n\n
+        If you really want global mutable state, try using static mut or a global UnsafeCell.
+
+const_eval_unallowed_mutable_refs_raw =
+    raw mutable references are not allowed in the final value of {$kind}s
+    .teach_note =
+        References in statics and constants may only refer to immutable values.\n\n
+        Statics are shared everywhere, and if they refer to mutable data one might violate memory
+        safety since holding multiple mutable references to shared data is not allowed.\n\n
+        If you really want global mutable state, try using static mut or a global UnsafeCell.
+
+const_eval_non_const_fmt_macro_call =
+    cannot call non-const formatting macro in {$kind}s
+
+const_eval_non_const_fn_call =
+    cannot call non-const fn `{$def_path_str}` in {$kind}s
+
+const_eval_unallowed_op_in_const_context =
+    {$msg}
+
+const_eval_unallowed_heap_allocations =
+    allocations are not allowed in {$kind}s
+    .label = allocation not allowed in {$kind}s
+    .teach_note =
+        The value of statics and constants must be known at compile time, and they live for the entire lifetime of a program. Creating a boxed value allocates memory on the heap at runtime, and therefore cannot be done at compile time.
+
+const_eval_unallowed_inline_asm =
+    inline assembly is not allowed in {$kind}s
+
+const_eval_interior_mutable_data_refer =
+    {$kind}s cannot refer to interior mutable data
+    .label = this borrow of an interior mutable value may end up in the final value
+    .help = to fix this, the value can be extracted to a separate `static` item and then referenced
+    .teach_note =
+        A constant containing interior mutable data behind a reference can allow you to modify that data.
+        This would make multiple uses of a constant to be able to see different values and allow circumventing
+        the `Send` and `Sync` requirements for shared mutable data, which is unsound.
+
+const_eval_interior_mutability_borrow =
+    cannot borrow here, since the borrowed element may contain interior mutability
diff --git a/compiler/rustc_error_messages/locales/en-US/driver.ftl b/compiler/rustc_error_messages/locales/en-US/driver.ftl
new file mode 100644
index 00000000000..73f084cf329
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/driver.ftl
@@ -0,0 +1,11 @@
+driver_rlink_unable_to_read = failed to read rlink file: `{$err}`
+
+driver_rlink_wrong_file_type = The input does not look like a .rlink file
+
+driver_rlink_empty_version_number = The input does not contain version number
+
+driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
+
+driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
+
+driver_rlink_no_a_file = rlink must be a file
diff --git a/compiler/rustc_error_messages/locales/en-US/lint.ftl b/compiler/rustc_error_messages/locales/en-US/lint.ftl
index 97317114716..27ad3e45366 100644
--- a/compiler/rustc_error_messages/locales/en-US/lint.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/lint.ftl
@@ -393,3 +393,37 @@ lint_builtin_deref_nullptr = dereferencing a null pointer
     .label = this code causes undefined behavior when executed
 
 lint_builtin_asm_labels = avoid using named labels in inline assembly
+
+lint_overruled_attribute = {$lint_level}({$lint_source}) incompatible with previous forbid
+    .label = overruled by previous forbid
+
+lint_default_source = `forbid` lint level is the default for {$id}
+
+lint_node_source = `forbid` level set here
+    .note = {$reason}
+
+lint_command_line_source = `forbid` lint level was set on command line
+
+lint_malformed_attribute = malformed lint attribute input
+
+lint_bad_attribute_argument = bad attribute argument
+
+lint_reason_must_be_string_literal = reason must be a string literal
+
+lint_reason_must_come_last = reason in lint attribute must come last
+
+lint_unknown_tool_in_scoped_lint = unknown tool name `{$tool_name}` found in scoped lint: `{$tool_name}::{$lint_name}`
+    .help = add `#![register_tool({$tool_name})]` to the crate root
+
+lint_unsupported_group = `{$lint_group}` lint group is not supported with ´--force-warn´
+
+lint_requested_level = requested on the command line with `{$level} {$lint_name}`
+
+lint_check_name_unknown = unknown lint: `{$lint_name}`
+    .help = did you mean: `{$suggestion}`
+
+lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
+
+lint_check_name_warning = {$msg}
+
+lint_check_name_deprecated = lint name `{$lint_name}` is deprecated and does not have an effect anymore. Use: {$new_name}
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl b/compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl
new file mode 100644
index 00000000000..98854152508
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/mir_dataflow.ftl
@@ -0,0 +1,29 @@
+mir_dataflow_path_must_end_in_filename =
+    path must end in a filename
+
+mir_dataflow_unknown_formatter =
+    unknown formatter
+
+mir_dataflow_duplicate_values_for =
+    duplicate values for `{$name}`
+
+mir_dataflow_requires_an_argument =
+    `{$name}` requires an argument
+
+mir_dataflow_stop_after_dataflow_ended_compilation =
+    stop_after_dataflow ended compilation
+
+mir_dataflow_peek_must_be_place_or_ref_place =
+    rustc_peek: argument expression must be either `place` or `&place`
+
+mir_dataflow_peek_must_be_not_temporary =
+    dataflow::sanity_check cannot feed a non-temp to rustc_peek
+
+mir_dataflow_peek_bit_not_set =
+    rustc_peek: bit not set
+
+mir_dataflow_peek_argument_not_a_local =
+    rustc_peek: argument was not a local
+
+mir_dataflow_peek_argument_untracked =
+    rustc_peek: argument untracked
diff --git a/compiler/rustc_error_messages/locales/en-US/ty_utils.ftl b/compiler/rustc_error_messages/locales/en-US/ty_utils.ftl
new file mode 100644
index 00000000000..1040ee1c97d
--- /dev/null
+++ b/compiler/rustc_error_messages/locales/en-US/ty_utils.ftl
@@ -0,0 +1,47 @@
+ty_utils_needs_drop_overflow = overflow while checking whether `{$query_ty}` requires drop
+
+ty_utils_generic_constant_too_complex = overly complex generic constant
+    .help = consider moving this anonymous constant into a `const` function
+    .maybe_supported = this operation may be supported in the future
+
+ty_utils_borrow_not_supported = borrowing is not supported in generic constants
+
+ty_utils_address_and_deref_not_supported = dereferencing or taking the address is not supported in generic constants
+
+ty_utils_array_not_supported = array construction is not supported in generic constants
+
+ty_utils_block_not_supported = blocks are not supported in generic constant
+
+ty_utils_never_to_any_not_supported = converting nevers to any is not supported in generic constant
+
+ty_utils_tuple_not_supported = tuple construction is not supported in generic constants
+
+ty_utils_index_not_supported = indexing is not supported in generic constant
+
+ty_utils_field_not_supported = field access is not supported in generic constant
+
+ty_utils_const_block_not_supported = const blocks are not supported in generic constant
+
+ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants
+
+ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants
+
+ty_utils_yield_not_supported = generator control flow is not allowed in generic constants
+
+ty_utils_loop_not_supported = loops and loop control flow are not supported in generic constants
+
+ty_utils_box_not_supported = allocations are not allowed in generic constants
+
+ty_utils_binary_not_supported = unsupported binary operation in generic constants
+
+ty_utils_logical_op_not_supported = unsupported operation in generic constants, short-circuiting operations would imply control flow
+
+ty_utils_assign_not_supported = assignment is not supported in generic constants
+
+ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants
+
+ty_utils_control_flow_not_supported = control flow is not supported in generic constants
+
+ty_utils_inline_asm_not_supported = assembly is not supported in generic constants
+
+ty_utils_operation_not_supported = unsupported operation in generic constant
diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs
index 2d001d445be..b18d1f553e4 100644
--- a/compiler/rustc_error_messages/src/lib.rs
+++ b/compiler/rustc_error_messages/src/lib.rs
@@ -32,10 +32,13 @@ pub use unic_langid::{langid, LanguageIdentifier};
 
 // Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
 fluent_messages! {
+    ast_lowering => "../locales/en-US/ast_lowering.ftl",
     ast_passes => "../locales/en-US/ast_passes.ftl",
+    attr => "../locales/en-US/attr.ftl",
     borrowck => "../locales/en-US/borrowck.ftl",
     builtin_macros => "../locales/en-US/builtin_macros.ftl",
     const_eval => "../locales/en-US/const_eval.ftl",
+    driver => "../locales/en-US/driver.ftl",
     expand => "../locales/en-US/expand.ftl",
     interface => "../locales/en-US/interface.ftl",
     lint => "../locales/en-US/lint.ftl",
@@ -44,7 +47,9 @@ fluent_messages! {
     plugin_impl => "../locales/en-US/plugin_impl.ftl",
     privacy => "../locales/en-US/privacy.ftl",
     save_analysis => "../locales/en-US/save_analysis.ftl",
+    ty_utils => "../locales/en-US/ty_utils.ftl",
     typeck => "../locales/en-US/typeck.ftl",
+    mir_dataflow => "../locales/en-US/mir_dataflow.ftl",
 }
 
 pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index bd58021f78f..af7ef96e485 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -21,6 +21,7 @@
 //! `late_lint_methods!` invocation in `lib.rs`.
 
 use crate::{
+    errors::BuiltinEllpisisInclusiveRangePatterns,
     types::{transparent_newtype_field, CItemKind},
     EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext,
 };
@@ -1760,18 +1761,11 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
                     None => format!("&(..={})", end),
                 };
                 if join.edition() >= Edition::Edition2021 {
-                    let mut err = cx.sess().struct_span_err_with_code(
-                        pat.span,
-                        msg,
-                        rustc_errors::error_code!(E0783),
-                    );
-                    err.span_suggestion(
-                        pat.span,
-                        suggestion,
+                    cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+                        span: pat.span,
+                        suggestion: pat.span,
                         replace,
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                    });
                 } else {
                     cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, pat.span, |lint| {
                         lint.build(msg)
@@ -1787,18 +1781,11 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns {
             } else {
                 let replace = "..=";
                 if join.edition() >= Edition::Edition2021 {
-                    let mut err = cx.sess().struct_span_err_with_code(
-                        pat.span,
-                        msg,
-                        rustc_errors::error_code!(E0783),
-                    );
-                    err.span_suggestion_short(
-                        join,
-                        suggestion,
-                        replace,
-                        Applicability::MachineApplicable,
-                    )
-                    .emit();
+                    cx.sess().emit_err(BuiltinEllpisisInclusiveRangePatterns {
+                        span: pat.span,
+                        suggestion: join,
+                        replace: replace.to_string(),
+                    });
                 } else {
                     cx.struct_span_lint(ELLIPSIS_INCLUSIVE_RANGE_PATTERNS, join, |lint| {
                         lint.build(msg)
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index 6586fe440f3..002bba4759b 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -16,12 +16,16 @@
 
 use self::TargetLint::*;
 
+use crate::errors::{
+    CheckNameDeprecated, CheckNameUnknown, CheckNameUnknownTool, CheckNameWarning, RequestedLevel,
+    UnsupportedGroup,
+};
 use crate::levels::LintLevelsBuilder;
 use crate::passes::{EarlyLintPassObject, LateLintPassObject};
 use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync;
-use rustc_errors::{add_elided_lifetime_in_path_suggestion, struct_span_err};
+use rustc_errors::add_elided_lifetime_in_path_suggestion;
 use rustc_errors::{
     Applicability, DecorateLint, LintDiagnosticBuilder, MultiSpan, SuggestionStyle,
 };
@@ -39,7 +43,7 @@ use rustc_session::lint::{FutureIncompatibleInfo, Level, Lint, LintBuffer, LintI
 use rustc_session::Session;
 use rustc_span::lev_distance::find_best_match_for_name;
 use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{BytePos, Span, DUMMY_SP};
+use rustc_span::{BytePos, Span};
 use rustc_target::abi;
 use tracing::debug;
 
@@ -326,68 +330,41 @@ impl LintStore {
     ) {
         let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
         if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn(_)) {
-            struct_span_err!(
-                sess,
-                DUMMY_SP,
-                E0602,
-                "`{}` lint group is not supported with ´--force-warn´",
-                crate::WARNINGS.name_lower()
-            )
-            .emit();
+            sess.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
             return;
         }
-        let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
-            CheckLintNameResult::Ok(_) => None,
-            CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
+        let lint_name = lint_name.to_string();
+        match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
+            CheckLintNameResult::Warning(msg, _) => {
+                sess.emit_warning(CheckNameWarning {
+                    msg,
+                    sub: RequestedLevel { level, lint_name },
+                });
+            }
             CheckLintNameResult::NoLint(suggestion) => {
-                let mut err =
-                    struct_span_err!(sess, DUMMY_SP, E0602, "unknown lint: `{}`", lint_name);
-
-                if let Some(suggestion) = suggestion {
-                    err.help(&format!("did you mean: `{}`", suggestion));
+                sess.emit_err(CheckNameUnknown {
+                    lint_name: lint_name.clone(),
+                    suggestion,
+                    sub: RequestedLevel { level, lint_name },
+                });
+            }
+            CheckLintNameResult::Tool(result) => {
+                if let Err((Some(_), new_name)) = result {
+                    sess.emit_warning(CheckNameDeprecated {
+                        lint_name: lint_name.clone(),
+                        new_name,
+                        sub: RequestedLevel { level, lint_name },
+                    });
                 }
-
-                Some(err.forget_guarantee())
             }
-            CheckLintNameResult::Tool(result) => match result {
-                Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
-                    "lint name `{}` is deprecated \
-                     and does not have an effect anymore. \
-                     Use: {}",
-                    lint_name, new_name
-                ))),
-                _ => None,
-            },
-            CheckLintNameResult::NoTool => Some(
-                struct_span_err!(
-                    sess,
-                    DUMMY_SP,
-                    E0602,
-                    "unknown lint tool: `{}`",
-                    tool_name.unwrap()
-                )
-                .forget_guarantee(),
-            ),
+            CheckLintNameResult::NoTool => {
+                sess.emit_err(CheckNameUnknownTool {
+                    tool_name: tool_name.unwrap(),
+                    sub: RequestedLevel { level, lint_name },
+                });
+            }
+            _ => {}
         };
-
-        if let Some(mut db) = db {
-            let msg = format!(
-                "requested on the command line with `{} {}`",
-                match level {
-                    Level::Allow => "-A",
-                    Level::Warn => "-W",
-                    Level::ForceWarn(_) => "--force-warn",
-                    Level::Deny => "-D",
-                    Level::Forbid => "-F",
-                    Level::Expect(_) => {
-                        unreachable!("lints with the level of `expect` should not run this code");
-                    }
-                },
-                lint_name
-            );
-            db.note(&msg);
-            db.emit();
-        }
     }
 
     /// True if this symbol represents a lint group name.
diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs
new file mode 100644
index 00000000000..606d8bda8aa
--- /dev/null
+++ b/compiler/rustc_lint/src/errors.rs
@@ -0,0 +1,162 @@
+use rustc_errors::{fluent, AddSubdiagnostic, ErrorGuaranteed};
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_session::{lint::Level, parse::ParseSess, SessionDiagnostic};
+use rustc_span::{Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::overruled_attribute, code = "E0453")]
+pub struct OverruledAttribute {
+    #[primary_span]
+    pub span: Span,
+    #[label]
+    pub overruled: Span,
+    pub lint_level: String,
+    pub lint_source: Symbol,
+    #[subdiagnostic]
+    pub sub: OverruledAttributeSub,
+}
+//
+pub enum OverruledAttributeSub {
+    DefaultSource { id: String },
+    NodeSource { span: Span, reason: Option<Symbol> },
+    CommandLineSource,
+}
+
+impl AddSubdiagnostic for OverruledAttributeSub {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        match self {
+            OverruledAttributeSub::DefaultSource { id } => {
+                diag.note(fluent::lint::default_source);
+                diag.set_arg("id", id);
+            }
+            OverruledAttributeSub::NodeSource { span, reason } => {
+                diag.span_label(span, fluent::lint::node_source);
+                if let Some(rationale) = reason {
+                    diag.note(rationale.as_str());
+                }
+            }
+            OverruledAttributeSub::CommandLineSource => {
+                diag.note(fluent::lint::command_line_source);
+            }
+        }
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::malformed_attribute, code = "E0452")]
+pub struct MalformedAttribute {
+    #[primary_span]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sub: MalformedAttributeSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum MalformedAttributeSub {
+    #[label(lint::bad_attribute_argument)]
+    BadAttributeArgument(#[primary_span] Span),
+    #[label(lint::reason_must_be_string_literal)]
+    ReasonMustBeStringLiteral(#[primary_span] Span),
+    #[label(lint::reason_must_come_last)]
+    ReasonMustComeLast(#[primary_span] Span),
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::unknown_tool_in_scoped_lint, code = "E0710")]
+pub struct UnknownToolInScopedLint {
+    #[primary_span]
+    pub span: Option<Span>,
+    pub tool_name: Symbol,
+    pub lint_name: String,
+    #[help]
+    pub is_nightly_build: Option<()>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::builtin_ellipsis_inclusive_range_patterns, code = "E0783")]
+pub struct BuiltinEllpisisInclusiveRangePatterns {
+    #[primary_span]
+    pub span: Span,
+    #[suggestion_short(code = "{replace}", applicability = "machine-applicable")]
+    pub suggestion: Span,
+    pub replace: String,
+}
+
+pub struct RequestedLevel {
+    pub level: Level,
+    pub lint_name: String,
+}
+
+impl AddSubdiagnostic for RequestedLevel {
+    fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
+        diag.note(fluent::lint::requested_level);
+        diag.set_arg(
+            "level",
+            match self.level {
+                Level::Allow => "-A",
+                Level::Warn => "-W",
+                Level::ForceWarn(_) => "--force-warn",
+                Level::Deny => "-D",
+                Level::Forbid => "-F",
+                Level::Expect(_) => {
+                    unreachable!("lints with the level of `expect` should not run this code");
+                }
+            },
+        );
+        diag.set_arg("lint_name", self.lint_name);
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::unsupported_group, code = "E0602")]
+pub struct UnsupportedGroup {
+    pub lint_group: String,
+}
+
+pub struct CheckNameUnknown {
+    pub lint_name: String,
+    pub suggestion: Option<Symbol>,
+    pub sub: RequestedLevel,
+}
+
+impl SessionDiagnostic<'_> for CheckNameUnknown {
+    fn into_diagnostic(
+        self,
+        sess: &ParseSess,
+    ) -> rustc_errors::DiagnosticBuilder<'_, ErrorGuaranteed> {
+        let mut diag = sess.struct_err(fluent::lint::check_name_unknown);
+        diag.code(rustc_errors::error_code!(E0602));
+        if let Some(suggestion) = self.suggestion {
+            diag.help(fluent::lint::help);
+            diag.set_arg("suggestion", suggestion);
+        }
+        diag.set_arg("lint_name", self.lint_name);
+        diag.subdiagnostic(self.sub);
+        diag
+    }
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::check_name_unknown_tool, code = "E0602")]
+pub struct CheckNameUnknownTool {
+    pub tool_name: Symbol,
+    #[subdiagnostic]
+    pub sub: RequestedLevel,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::check_name_warning)]
+pub struct CheckNameWarning {
+    pub msg: String,
+    #[subdiagnostic]
+    pub sub: RequestedLevel,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(lint::check_name_deprecated)]
+pub struct CheckNameDeprecated {
+    pub lint_name: String,
+    pub new_name: String,
+    #[subdiagnostic]
+    pub sub: RequestedLevel,
+}
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 1cabb58bbeb..89409b58f88 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -3,7 +3,7 @@ use crate::late::unerased_lint_store;
 use rustc_ast as ast;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan};
+use rustc_errors::{Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan};
 use rustc_hir as hir;
 use rustc_hir::{intravisit, HirId};
 use rustc_middle::hir::nested_filter;
@@ -23,6 +23,11 @@ use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
 use tracing::debug;
 
+use crate::errors::{
+    MalformedAttribute, MalformedAttributeSub, OverruledAttribute, OverruledAttributeSub,
+    UnknownToolInScopedLint,
+};
+
 fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
     let store = unerased_lint_store(tcx);
     let levels =
@@ -186,16 +191,26 @@ impl<'s> LintLevelsBuilder<'s> {
                     }
                 };
                 if !fcw_warning {
-                    let mut diag_builder = struct_span_err!(
-                        self.sess,
-                        src.span(),
-                        E0453,
-                        "{}({}) incompatible with previous forbid",
-                        level.as_str(),
-                        src.name(),
-                    );
-                    decorate_diag(&mut diag_builder);
-                    diag_builder.emit();
+                    self.sess.emit_err(OverruledAttribute {
+                        span: src.span(),
+                        overruled: src.span(),
+                        lint_level: level.as_str().to_string(),
+                        lint_source: src.name(),
+                        sub: match old_src {
+                            LintLevelSource::Default => {
+                                OverruledAttributeSub::DefaultSource { id: id.to_string() }
+                            }
+                            LintLevelSource::Node(_, forbid_source_span, reason) => {
+                                OverruledAttributeSub::NodeSource {
+                                    span: forbid_source_span,
+                                    reason,
+                                }
+                            }
+                            LintLevelSource::CommandLine(_, _) => {
+                                OverruledAttributeSub::CommandLineSource
+                            }
+                        },
+                    });
                 } else {
                     self.struct_lint(
                         FORBIDDEN_LINT_GROUPS,
@@ -266,7 +281,6 @@ impl<'s> LintLevelsBuilder<'s> {
         self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
 
         let sess = self.sess;
-        let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
         for (attr_index, attr) in attrs.iter().enumerate() {
             if attr.has_name(sym::automatically_derived) {
                 self.current_specs_mut().insert(
@@ -317,20 +331,27 @@ impl<'s> LintLevelsBuilder<'s> {
                                 }
                                 reason = Some(rationale);
                             } else {
-                                bad_attr(name_value.span)
-                                    .span_label(name_value.span, "reason must be a string literal")
-                                    .emit();
+                                sess.emit_err(MalformedAttribute {
+                                    span: name_value.span,
+                                    sub: MalformedAttributeSub::ReasonMustBeStringLiteral(
+                                        name_value.span,
+                                    ),
+                                });
                             }
                             // found reason, reslice meta list to exclude it
                             metas.pop().unwrap();
                         } else {
-                            bad_attr(item.span)
-                                .span_label(item.span, "bad attribute argument")
-                                .emit();
+                            sess.emit_err(MalformedAttribute {
+                                span: item.span,
+                                sub: MalformedAttributeSub::BadAttributeArgument(item.span),
+                            });
                         }
                     }
                     ast::MetaItemKind::List(_) => {
-                        bad_attr(item.span).span_label(item.span, "bad attribute argument").emit();
+                        sess.emit_err(MalformedAttribute {
+                            span: item.span,
+                            sub: MalformedAttributeSub::BadAttributeArgument(item.span),
+                        });
                     }
                 }
             }
@@ -348,20 +369,21 @@ impl<'s> LintLevelsBuilder<'s> {
                 let meta_item = match li {
                     ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item,
                     _ => {
-                        let mut err = bad_attr(sp);
-                        let mut add_label = true;
                         if let Some(item) = li.meta_item() {
                             if let ast::MetaItemKind::NameValue(_) = item.kind {
                                 if item.path == sym::reason {
-                                    err.span_label(sp, "reason in lint attribute must come last");
-                                    add_label = false;
+                                    sess.emit_err(MalformedAttribute {
+                                        span: sp,
+                                        sub: MalformedAttributeSub::ReasonMustComeLast(sp),
+                                    });
+                                    continue;
                                 }
                             }
                         }
-                        if add_label {
-                            err.span_label(sp, "bad attribute argument");
-                        }
-                        err.emit();
+                        sess.emit_err(MalformedAttribute {
+                            span: sp,
+                            sub: MalformedAttributeSub::BadAttributeArgument(sp),
+                        });
                         continue;
                     }
                 };
@@ -485,22 +507,12 @@ impl<'s> LintLevelsBuilder<'s> {
                     }
 
                     &CheckLintNameResult::NoTool => {
-                        let mut err = struct_span_err!(
-                            sess,
-                            tool_ident.map_or(DUMMY_SP, |ident| ident.span),
-                            E0710,
-                            "unknown tool name `{}` found in scoped lint: `{}::{}`",
-                            tool_name.unwrap(),
-                            tool_name.unwrap(),
-                            pprust::path_to_string(&meta_item.path),
-                        );
-                        if sess.is_nightly_build() {
-                            err.help(&format!(
-                                "add `#![register_tool({})]` to the crate root",
-                                tool_name.unwrap()
-                            ));
-                        }
-                        err.emit();
+                        sess.emit_err(UnknownToolInScopedLint {
+                            span: tool_ident.map(|ident| ident.span),
+                            tool_name: tool_name.unwrap(),
+                            lint_name: pprust::path_to_string(&meta_item.path),
+                            is_nightly_build: sess.is_nightly_build().then_some(()),
+                        });
                         continue;
                     }
 
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index f087c624917..f34e062fd12 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -47,6 +47,7 @@ pub mod builtin;
 mod context;
 mod early;
 mod enum_intrinsics_non_enums;
+mod errors;
 mod expect;
 pub mod hidden_unicode_codepoints;
 mod internal;
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index 7ab71f9009d..9da9b4e91f6 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -1531,6 +1531,7 @@ impl<'tcx> Place<'tcx> {
 }
 
 impl From<Local> for Place<'_> {
+    #[inline]
     fn from(local: Local) -> Self {
         Place { local, projection: List::empty() }
     }
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 5e26a52900e..0f2504e3d9a 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -15,50 +15,33 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::RangeEnd;
 use rustc_index::newtype_index;
 use rustc_index::vec::IndexVec;
-use rustc_middle::infer::canonical::Canonical;
 use rustc_middle::middle::region;
 use rustc_middle::mir::interpret::AllocId;
 use rustc_middle::mir::{self, BinOp, BorrowKind, FakeReadCause, Field, Mutability, UnOp};
 use rustc_middle::ty::adjustment::PointerCast;
 use rustc_middle::ty::subst::SubstsRef;
-use rustc_middle::ty::CanonicalUserTypeAnnotation;
-use rustc_middle::ty::{self, AdtDef, Ty, UpvarSubsts, UserType};
+use rustc_middle::ty::{self, AdtDef, Ty, UpvarSubsts};
+use rustc_middle::ty::{CanonicalUserType, CanonicalUserTypeAnnotation};
+use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, Symbol, DUMMY_SP};
 use rustc_target::abi::VariantIdx;
 use rustc_target::asm::InlineAsmRegOrRegClass;
-
-use rustc_span::def_id::LocalDefId;
 use std::fmt;
 use std::ops::Index;
 
 pub mod visit;
 
-newtype_index! {
-    /// An index to an [`Arm`] stored in [`Thir::arms`]
-    #[derive(HashStable)]
-    pub struct ArmId {
-        DEBUG_FORMAT = "a{}"
-    }
-}
-
-newtype_index! {
-    /// An index to an [`Expr`] stored in [`Thir::exprs`]
-    #[derive(HashStable)]
-    pub struct ExprId {
-        DEBUG_FORMAT = "e{}"
-    }
-}
-
-newtype_index! {
-    #[derive(HashStable)]
-    /// An index to a [`Stmt`] stored in [`Thir::stmts`]
-    pub struct StmtId {
-        DEBUG_FORMAT = "s{}"
-    }
-}
-
 macro_rules! thir_with_elements {
-    ($($name:ident: $id:ty => $value:ty,)*) => {
+    ($($name:ident: $id:ty => $value:ty => $format:literal,)*) => {
+        $(
+            newtype_index! {
+                #[derive(HashStable)]
+                pub struct $id {
+                    DEBUG_FORMAT = $format
+                }
+            }
+        )*
+
         /// A container for a THIR body.
         ///
         /// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
@@ -91,9 +74,10 @@ macro_rules! thir_with_elements {
 }
 
 thir_with_elements! {
-    arms: ArmId => Arm<'tcx>,
-    exprs: ExprId => Expr<'tcx>,
-    stmts: StmtId => Stmt<'tcx>,
+    arms: ArmId => Arm<'tcx> => "a{}",
+    blocks: BlockId => Block => "b{}",
+    exprs: ExprId => Expr<'tcx> => "e{}",
+    stmts: StmtId => Stmt<'tcx> => "s{}",
 }
 
 #[derive(Copy, Clone, Debug, HashStable)]
@@ -121,8 +105,10 @@ pub struct Block {
     pub safety_mode: BlockSafety,
 }
 
+type UserTy<'tcx> = Option<Box<CanonicalUserType<'tcx>>>;
+
 #[derive(Clone, Debug, HashStable)]
-pub struct Adt<'tcx> {
+pub struct AdtExpr<'tcx> {
     /// The ADT we're constructing.
     pub adt_def: AdtDef<'tcx>,
     /// The variant of the ADT.
@@ -131,13 +117,30 @@ pub struct Adt<'tcx> {
 
     /// Optional user-given substs: for something like `let x =
     /// Bar::<T> { ... }`.
-    pub user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+    pub user_ty: UserTy<'tcx>,
 
     pub fields: Box<[FieldExpr]>,
     /// The base, e.g. `Foo {x: 1, .. base}`.
     pub base: Option<FruInfo<'tcx>>,
 }
 
+#[derive(Clone, Debug, HashStable)]
+pub struct ClosureExpr<'tcx> {
+    pub closure_id: LocalDefId,
+    pub substs: UpvarSubsts<'tcx>,
+    pub upvars: Box<[ExprId]>,
+    pub movability: Option<hir::Movability>,
+    pub fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
+}
+
+#[derive(Clone, Debug, HashStable)]
+pub struct InlineAsmExpr<'tcx> {
+    pub template: &'tcx [InlineAsmTemplatePiece],
+    pub operands: Box<[InlineAsmOperand<'tcx>]>,
+    pub options: InlineAsmOptions,
+    pub line_spans: &'tcx [Span],
+}
+
 #[derive(Copy, Clone, Debug, HashStable)]
 pub enum BlockSafety {
     Safe,
@@ -183,7 +186,7 @@ pub enum StmtKind<'tcx> {
         initializer: Option<ExprId>,
 
         /// `let pat: ty = <INIT> else { <ELSE> }
-        else_block: Option<Block>,
+        else_block: Option<BlockId>,
 
         /// The lint level for this `let` statement.
         lint_level: LintLevel,
@@ -307,7 +310,7 @@ pub enum ExprKind<'tcx> {
     },
     /// A block.
     Block {
-        body: Block,
+        block: BlockId,
     },
     /// An assignment: `lhs = rhs`.
     Assign {
@@ -387,27 +390,21 @@ pub enum ExprKind<'tcx> {
         fields: Box<[ExprId]>,
     },
     /// An ADT constructor, e.g. `Foo {x: 1, y: 2}`.
-    Adt(Box<Adt<'tcx>>),
+    Adt(Box<AdtExpr<'tcx>>),
     /// A type ascription on a place.
     PlaceTypeAscription {
         source: ExprId,
         /// Type that the user gave to this expression
-        user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        user_ty: UserTy<'tcx>,
     },
     /// A type ascription on a value, e.g. `42: i32`.
     ValueTypeAscription {
         source: ExprId,
         /// Type that the user gave to this expression
-        user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        user_ty: UserTy<'tcx>,
     },
     /// A closure definition.
-    Closure {
-        closure_id: LocalDefId,
-        substs: UpvarSubsts<'tcx>,
-        upvars: Box<[ExprId]>,
-        movability: Option<hir::Movability>,
-        fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
-    },
+    Closure(Box<ClosureExpr<'tcx>>),
     /// A literal.
     Literal {
         lit: &'tcx hir::Lit,
@@ -416,17 +413,17 @@ pub enum ExprKind<'tcx> {
     /// For literals that don't correspond to anything in the HIR
     NonHirLiteral {
         lit: ty::ScalarInt,
-        user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        user_ty: UserTy<'tcx>,
     },
     /// A literal of a ZST type.
     ZstLiteral {
-        user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        user_ty: UserTy<'tcx>,
     },
     /// Associated constants and named constants
     NamedConst {
         def_id: DefId,
         substs: SubstsRef<'tcx>,
-        user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
+        user_ty: UserTy<'tcx>,
     },
     ConstParam {
         param: ty::ParamConst,
@@ -443,12 +440,7 @@ pub enum ExprKind<'tcx> {
         def_id: DefId,
     },
     /// Inline assembly, i.e. `asm!()`.
-    InlineAsm {
-        template: &'tcx [InlineAsmTemplatePiece],
-        operands: Box<[InlineAsmOperand<'tcx>]>,
-        options: InlineAsmOptions,
-        line_spans: &'tcx [Span],
-    },
+    InlineAsm(Box<InlineAsmExpr<'tcx>>),
     /// An expression taking a reference to a thread local.
     ThreadLocalRef(DefId),
     /// A `yield` expression.
@@ -815,7 +807,10 @@ mod size_asserts {
     use super::*;
     // These are in alphabetical order, which is easy to maintain.
     static_assert_size!(Block, 56);
-    static_assert_size!(Expr<'_>, 104);
+    static_assert_size!(Expr<'_>, 64);
+    static_assert_size!(ExprKind<'_>, 40);
     static_assert_size!(Pat<'_>, 24);
-    static_assert_size!(Stmt<'_>, 120);
+    static_assert_size!(PatKind<'_>, 112);
+    static_assert_size!(Stmt<'_>, 72);
+    static_assert_size!(StmtKind<'_>, 64);
 }
diff --git a/compiler/rustc_middle/src/thir/visit.rs b/compiler/rustc_middle/src/thir/visit.rs
index 97249fdd175..c5c48a63609 100644
--- a/compiler/rustc_middle/src/thir/visit.rs
+++ b/compiler/rustc_middle/src/thir/visit.rs
@@ -1,5 +1,6 @@
 use super::{
-    Arm, Block, Expr, ExprKind, Guard, InlineAsmOperand, Pat, PatKind, Stmt, StmtKind, Thir,
+    AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, Guard, InlineAsmExpr, InlineAsmOperand, Pat,
+    PatKind, Stmt, StmtKind, Thir,
 };
 
 pub trait Visitor<'a, 'tcx: 'a>: Sized {
@@ -75,7 +76,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
                 visitor.visit_arm(&visitor.thir()[arm]);
             }
         }
-        Block { ref body } => visitor.visit_block(body),
+        Block { block } => visitor.visit_block(&visitor.thir()[block]),
         Assign { lhs, rhs } | AssignOp { lhs, rhs, op: _ } => {
             visitor.visit_expr(&visitor.thir()[lhs]);
             visitor.visit_expr(&visitor.thir()[rhs]);
@@ -108,7 +109,7 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
                 visitor.visit_expr(&visitor.thir()[field]);
             }
         }
-        Adt(box crate::thir::Adt {
+        Adt(box AdtExpr {
             ref fields,
             ref base,
             adt_def: _,
@@ -126,14 +127,20 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
         PlaceTypeAscription { source, user_ty: _ } | ValueTypeAscription { source, user_ty: _ } => {
             visitor.visit_expr(&visitor.thir()[source])
         }
-        Closure { closure_id: _, substs: _, upvars: _, movability: _, fake_reads: _ } => {}
+        Closure(box ClosureExpr {
+            closure_id: _,
+            substs: _,
+            upvars: _,
+            movability: _,
+            fake_reads: _,
+        }) => {}
         Literal { lit: _, neg: _ } => {}
         NonHirLiteral { lit: _, user_ty: _ } => {}
         ZstLiteral { user_ty: _ } => {}
         NamedConst { def_id: _, substs: _, user_ty: _ } => {}
         ConstParam { param: _, def_id: _ } => {}
         StaticRef { alloc_id: _, ty: _, def_id: _ } => {}
-        InlineAsm { ref operands, template: _, options: _, line_spans: _ } => {
+        InlineAsm(box InlineAsmExpr { ref operands, template: _, options: _, line_spans: _ }) => {
             for op in &**operands {
                 use InlineAsmOperand::*;
                 match op {
@@ -174,7 +181,7 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
             }
             visitor.visit_pat(pattern);
             if let Some(block) = else_block {
-                visitor.visit_block(block)
+                visitor.visit_block(&visitor.thir()[*block])
             }
         }
     }
diff --git a/compiler/rustc_mir_build/src/build/block.rs b/compiler/rustc_mir_build/src/build/block.rs
index 280b6aad12c..d5213dc0e04 100644
--- a/compiler/rustc_mir_build/src/build/block.rs
+++ b/compiler/rustc_mir_build/src/build/block.rs
@@ -11,7 +11,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         &mut self,
         destination: Place<'tcx>,
         block: BasicBlock,
-        ast_block: &Block,
+        ast_block: BlockId,
         source_info: SourceInfo,
     ) -> BlockAnd<()> {
         let Block {
@@ -22,7 +22,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             expr,
             targeted_by_break,
             safety_mode,
-        } = *ast_block;
+        } = self.thir[ast_block];
         let expr = expr.map(|expr| &self.thir[expr]);
         self.in_opt_scope(opt_destruction_scope.map(|de| (de, source_info)), move |this| {
             this.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
@@ -146,7 +146,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                                                 block,
                                                 init,
                                                 initializer_span,
-                                                else_block,
+                                                *else_block,
                                                 visibility_scope,
                                                 last_remainder_scope,
                                                 remainder_span,
diff --git a/compiler/rustc_mir_build/src/build/expr/as_constant.rs b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
index 19d420f154d..b316a6eeac1 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_constant.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_constant.rs
@@ -41,11 +41,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 Constant { span, user_ty: None, literal }
             }
-            ExprKind::NonHirLiteral { lit, user_ty } => {
-                let user_ty = user_ty.map(|user_ty| {
+            ExprKind::NonHirLiteral { lit, ref user_ty } => {
+                let user_ty = user_ty.as_ref().map(|box user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
-                        user_ty,
+                        user_ty: *user_ty,
                         inferred_ty: ty,
                     })
                 });
@@ -53,11 +53,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 Constant { span, user_ty: user_ty, literal }
             }
-            ExprKind::ZstLiteral { user_ty } => {
-                let user_ty = user_ty.map(|user_ty| {
+            ExprKind::ZstLiteral { ref user_ty } => {
+                let user_ty = user_ty.as_ref().map(|box user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
-                        user_ty,
+                        user_ty: *user_ty,
                         inferred_ty: ty,
                     })
                 });
@@ -65,11 +65,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 Constant { span, user_ty: user_ty, literal }
             }
-            ExprKind::NamedConst { def_id, substs, user_ty } => {
-                let user_ty = user_ty.map(|user_ty| {
+            ExprKind::NamedConst { def_id, substs, ref user_ty } => {
+                let user_ty = user_ty.as_ref().map(|box user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span,
-                        user_ty,
+                        user_ty: *user_ty,
                         inferred_ty: ty,
                     })
                 });
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index 0c06aad4e44..b8277f28cdc 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -513,7 +513,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 block.and(place_builder)
             }
 
-            ExprKind::PlaceTypeAscription { source, user_ty } => {
+            ExprKind::PlaceTypeAscription { source, ref user_ty } => {
                 let place_builder = unpack!(
                     block = this.expr_as_place(
                         block,
@@ -522,11 +522,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                         fake_borrow_temps,
                     )
                 );
-                if let Some(user_ty) = user_ty {
+                if let Some(box user_ty) = user_ty {
                     let annotation_index =
                         this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                             span: source_info.span,
-                            user_ty,
+                            user_ty: *user_ty,
                             inferred_ty: expr.ty,
                         });
 
@@ -547,15 +547,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 }
                 block.and(place_builder)
             }
-            ExprKind::ValueTypeAscription { source, user_ty } => {
+            ExprKind::ValueTypeAscription { source, ref user_ty } => {
                 let source = &this.thir[source];
                 let temp =
                     unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
-                if let Some(user_ty) = user_ty {
+                if let Some(box user_ty) = user_ty {
                     let annotation_index =
                         this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                             span: source_info.span,
-                            user_ty,
+                            user_ty: *user_ty,
                             inferred_ty: expr.ty,
                         });
                     this.cfg.push(
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 15f2d17c4e0..4b232a1b515 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -302,7 +302,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                 block.and(Rvalue::Aggregate(Box::new(AggregateKind::Tuple), fields))
             }
-            ExprKind::Closure { closure_id, substs, ref upvars, movability, ref fake_reads } => {
+            ExprKind::Closure(box ClosureExpr {
+                closure_id,
+                substs,
+                ref upvars,
+                movability,
+                ref fake_reads,
+            }) => {
                 // Convert the closure fake reads, if any, from `ExprRef` to mir `Place`
                 // and push the fake reads.
                 // This must come before creating the operands. This is required in case
diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
index 724b72f8769..e5dafb820bf 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_temp.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs
@@ -83,8 +83,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // Don't bother with StorageLive and Dead for these temporaries,
             // they are never assigned.
             ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
-            ExprKind::Block { body: Block { expr: None, targeted_by_break: false, .. } }
-                if expr_ty.is_never() => {}
+            ExprKind::Block { block }
+                if let Block { expr: None, targeted_by_break: false, .. } = this.thir[block]
+                    && expr_ty.is_never() => {}
             _ => {
                 this.cfg
                     .push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index 017d43d10a9..48ec7a06724 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -46,7 +46,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     })
                 })
             }
-            ExprKind::Block { body: ref ast_block } => {
+            ExprKind::Block { block: ast_block } => {
                 this.ast_block(destination, block, ast_block, source_info)
             }
             ExprKind::Match { scrutinee, ref arms } => {
@@ -314,11 +314,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 this.cfg.push_assign(block, source_info, destination, address_of);
                 block.unit()
             }
-            ExprKind::Adt(box Adt {
+            ExprKind::Adt(box AdtExpr {
                 adt_def,
                 variant_index,
                 substs,
-                user_ty,
+                ref user_ty,
                 ref fields,
                 ref base,
             }) => {
@@ -378,10 +378,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 };
 
                 let inferred_ty = expr.ty;
-                let user_ty = user_ty.map(|ty| {
+                let user_ty = user_ty.as_ref().map(|box user_ty| {
                     this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
                         span: source_info.span,
-                        user_ty: ty,
+                        user_ty: *user_ty,
                         inferred_ty,
                     })
                 });
@@ -400,7 +400,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 );
                 block.unit()
             }
-            ExprKind::InlineAsm { template, ref operands, options, line_spans } => {
+            ExprKind::InlineAsm(box InlineAsmExpr {
+                template,
+                ref operands,
+                options,
+                line_spans,
+            }) => {
                 use rustc_middle::{mir, thir};
                 let operands = operands
                     .into_iter()
diff --git a/compiler/rustc_mir_build/src/build/expr/stmt.rs b/compiler/rustc_mir_build/src/build/expr/stmt.rs
index a7e1331aabc..00dbcaeb0c9 100644
--- a/compiler/rustc_mir_build/src/build/expr/stmt.rs
+++ b/compiler/rustc_mir_build/src/build/expr/stmt.rs
@@ -116,14 +116,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // it is usually better to focus on `the_value` rather
                 // than the entirety of block(s) surrounding it.
                 let adjusted_span = (|| {
-                    if let ExprKind::Block { body } = &expr.kind && let Some(tail_ex) = body.expr {
+                    if let ExprKind::Block { block } = expr.kind
+                        && let Some(tail_ex) = this.thir[block].expr
+                    {
                         let mut expr = &this.thir[tail_ex];
-                        while let ExprKind::Block {
-                            body: Block { expr: Some(nested_expr), .. },
-                        }
-                        | ExprKind::Scope { value: nested_expr, .. } = expr.kind
-                        {
-                            expr = &this.thir[nested_expr];
+                        loop {
+                            match expr.kind {
+                                ExprKind::Block { block }
+                                    if let Some(nested_expr) = this.thir[block].expr =>
+                                {
+                                    expr = &this.thir[nested_expr];
+                                }
+                                ExprKind::Scope { value: nested_expr, .. } => {
+                                    expr = &this.thir[nested_expr];
+                                }
+                                _ => break,
+                            }
                         }
                         this.block_context.push(BlockFrame::TailExpr {
                             tail_result_is_ignored: true,
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index ce38283724d..080dab03031 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -2280,15 +2280,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         mut block: BasicBlock,
         init: &Expr<'tcx>,
         initializer_span: Span,
-        else_block: &Block,
+        else_block: BlockId,
         visibility_scope: Option<SourceScope>,
         remainder_scope: region::Scope,
         remainder_span: Span,
         pattern: &Pat<'tcx>,
     ) -> BlockAnd<()> {
+        let else_block_span = self.thir[else_block].span;
         let (matching, failure) = self.in_if_then_scope(remainder_scope, |this| {
             let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
-            let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
+            let pat = Pat { ty: init.ty, span: else_block_span, kind: Box::new(PatKind::Wild) };
             let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
             this.declare_bindings(
                 visibility_scope,
@@ -2318,7 +2319,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             );
             // This block is for the failure case
             let failure = this.bind_pattern(
-                this.source_info(else_block.span),
+                this.source_info(else_block_span),
                 wildcard,
                 None,
                 &fake_borrow_temps,
@@ -2334,19 +2335,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // This place is not really used because this destination place
         // should never be used to take values at the end of the failure
         // block.
-        let dummy_place = self.temp(self.tcx.types.never, else_block.span);
+        let dummy_place = self.temp(self.tcx.types.never, else_block_span);
         let failure_block;
         unpack!(
             failure_block = self.ast_block(
                 dummy_place,
                 failure,
                 else_block,
-                self.source_info(else_block.span),
+                self.source_info(else_block_span),
             )
         );
         self.cfg.terminate(
             failure_block,
-            self.source_info(else_block.span),
+            self.source_info(else_block_span),
             TerminatorKind::Unreachable,
         );
         matching.unit()
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index f0b0456c4b9..bf5a2e7c73f 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -391,7 +391,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
             ExprKind::InlineAsm { .. } => {
                 self.requires_unsafe(expr.span, UseOfInlineAssembly);
             }
-            ExprKind::Adt(box Adt {
+            ExprKind::Adt(box AdtExpr {
                 adt_def,
                 variant_index: _,
                 substs: _,
@@ -402,13 +402,13 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
                 (Bound::Unbounded, Bound::Unbounded) => {}
                 _ => self.requires_unsafe(expr.span, InitializingTypeWith),
             },
-            ExprKind::Closure {
+            ExprKind::Closure(box ClosureExpr {
                 closure_id,
                 substs: _,
                 upvars: _,
                 movability: _,
                 fake_reads: _,
-            } => {
+            }) => {
                 let closure_def = if let Some((did, const_param_id)) =
                     ty::WithOptConstParam::try_lookup(closure_id, self.tcx)
                 {
diff --git a/compiler/rustc_mir_build/src/thir/cx/block.rs b/compiler/rustc_mir_build/src/thir/cx/block.rs
index dccaa61ed89..54c4b9eda70 100644
--- a/compiler/rustc_mir_build/src/thir/cx/block.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/block.rs
@@ -9,13 +9,13 @@ use rustc_index::vec::Idx;
 use rustc_middle::ty::CanonicalUserTypeAnnotation;
 
 impl<'tcx> Cx<'tcx> {
-    pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> Block {
+    pub(crate) fn mirror_block(&mut self, block: &'tcx hir::Block<'tcx>) -> BlockId {
         // We have to eagerly lower the "spine" of the statements
         // in order to get the lexical scoping correctly.
         let stmts = self.mirror_stmts(block.hir_id.local_id, block.stmts);
         let opt_destruction_scope =
             self.region_scope_tree.opt_destruction_scope(block.hir_id.local_id);
-        Block {
+        let block = Block {
             targeted_by_break: block.targeted_by_break,
             region_scope: region::Scope {
                 id: block.hir_id.local_id,
@@ -34,7 +34,9 @@ impl<'tcx> Cx<'tcx> {
                     BlockSafety::ExplicitUnsafe(block.hir_id)
                 }
             },
-        }
+        };
+
+        self.thir.blocks.push(block)
     }
 
     fn mirror_stmts(
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index 985601712c4..0c2b117453f 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -108,8 +108,8 @@ impl<'tcx> Cx<'tcx> {
         //   // ^ error message points at this expression.
         // }
         let mut adjust_span = |expr: &mut Expr<'tcx>| {
-            if let ExprKind::Block { body } = &expr.kind {
-                if let Some(last_expr) = body.expr {
+            if let ExprKind::Block { block } = expr.kind {
+                if let Some(last_expr) = self.thir[block].expr {
                     span = self.thir[last_expr].span;
                     expr.span = span;
                 }
@@ -329,7 +329,7 @@ impl<'tcx> Cx<'tcx> {
                                 if let UserType::TypeOf(ref mut did, _) = &mut u_ty.value {
                                     *did = adt_def.did();
                                 }
-                                u_ty
+                                Box::new(u_ty)
                             });
                         debug!("make_mirror_unadjusted: (call) user_ty={:?}", user_ty);
 
@@ -341,7 +341,7 @@ impl<'tcx> Cx<'tcx> {
                                 expr: self.mirror_expr(e),
                             })
                             .collect();
-                        ExprKind::Adt(Box::new(Adt {
+                        ExprKind::Adt(Box::new(AdtExpr {
                             adt_def,
                             substs,
                             variant_index: index,
@@ -369,7 +369,7 @@ impl<'tcx> Cx<'tcx> {
                 ExprKind::AddressOf { mutability, arg: self.mirror_expr(arg) }
             }
 
-            hir::ExprKind::Block(ref blk, _) => ExprKind::Block { body: self.mirror_block(blk) },
+            hir::ExprKind::Block(ref blk, _) => ExprKind::Block { block: self.mirror_block(blk) },
 
             hir::ExprKind::Assign(ref lhs, ref rhs, _) => {
                 ExprKind::Assign { lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs) }
@@ -464,9 +464,9 @@ impl<'tcx> Cx<'tcx> {
                 ty::Adt(adt, substs) => match adt.adt_kind() {
                     AdtKind::Struct | AdtKind::Union => {
                         let user_provided_types = self.typeck_results().user_provided_types();
-                        let user_ty = user_provided_types.get(expr.hir_id).copied();
+                        let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
                         debug!("make_mirror_unadjusted: (struct/union) user_ty={:?}", user_ty);
-                        ExprKind::Adt(Box::new(Adt {
+                        ExprKind::Adt(Box::new(AdtExpr {
                             adt_def: *adt,
                             variant_index: VariantIdx::new(0),
                             substs,
@@ -490,9 +490,10 @@ impl<'tcx> Cx<'tcx> {
                                 let index = adt.variant_index_with_id(variant_id);
                                 let user_provided_types =
                                     self.typeck_results().user_provided_types();
-                                let user_ty = user_provided_types.get(expr.hir_id).copied();
+                                let user_ty =
+                                    user_provided_types.get(expr.hir_id).copied().map(Box::new);
                                 debug!("make_mirror_unadjusted: (variant) user_ty={:?}", user_ty);
-                                ExprKind::Adt(Box::new(Adt {
+                                ExprKind::Adt(Box::new(AdtExpr {
                                     adt_def: *adt,
                                     variant_index: index,
                                     substs,
@@ -547,7 +548,13 @@ impl<'tcx> Cx<'tcx> {
                     None => Vec::new(),
                 };
 
-                ExprKind::Closure { closure_id: def_id, substs, upvars, movability, fake_reads }
+                ExprKind::Closure(Box::new(ClosureExpr {
+                    closure_id: def_id,
+                    substs,
+                    upvars,
+                    movability,
+                    fake_reads,
+                }))
             }
 
             hir::ExprKind::Path(ref qpath) => {
@@ -555,7 +562,7 @@ impl<'tcx> Cx<'tcx> {
                 self.convert_path_expr(expr, res)
             }
 
-            hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm {
+            hir::ExprKind::InlineAsm(ref asm) => ExprKind::InlineAsm(Box::new(InlineAsmExpr {
                 template: asm.template,
                 operands: asm
                     .operands
@@ -614,7 +621,7 @@ impl<'tcx> Cx<'tcx> {
                     .collect(),
                 options: asm.options,
                 line_spans: asm.line_spans,
-            },
+            })),
 
             hir::ExprKind::ConstBlock(ref anon_const) => {
                 let ty = self.typeck_results().node_type(anon_const.hir_id);
@@ -679,8 +686,8 @@ impl<'tcx> Cx<'tcx> {
                 let body = self.thir.exprs.push(Expr {
                     ty: block_ty,
                     temp_lifetime,
-                    span: block.span,
-                    kind: ExprKind::Block { body: block },
+                    span: self.thir[block].span,
+                    kind: ExprKind::Block { block },
                 });
                 ExprKind::Loop { body }
             }
@@ -712,14 +719,17 @@ impl<'tcx> Cx<'tcx> {
                     });
                     debug!("make_mirror_unadjusted: (cast) user_ty={:?}", user_ty);
 
-                    ExprKind::ValueTypeAscription { source: cast_expr, user_ty: Some(*user_ty) }
+                    ExprKind::ValueTypeAscription {
+                        source: cast_expr,
+                        user_ty: Some(Box::new(*user_ty)),
+                    }
                 } else {
                     cast
                 }
             }
             hir::ExprKind::Type(ref source, ref ty) => {
                 let user_provided_types = self.typeck_results.user_provided_types();
-                let user_ty = user_provided_types.get(ty.hir_id).copied();
+                let user_ty = user_provided_types.get(ty.hir_id).copied().map(Box::new);
                 debug!("make_mirror_unadjusted: (type) user_ty={:?}", user_ty);
                 let mirrored = self.mirror_expr(source);
                 if source.is_syntactic_place_expr() {
@@ -748,7 +758,7 @@ impl<'tcx> Cx<'tcx> {
         &mut self,
         hir_id: hir::HirId,
         res: Res,
-    ) -> Option<ty::CanonicalUserType<'tcx>> {
+    ) -> Option<Box<ty::CanonicalUserType<'tcx>>> {
         debug!("user_substs_applied_to_res: res={:?}", res);
         let user_provided_type = match res {
             // A reference to something callable -- e.g., a fn, method, or
@@ -759,7 +769,7 @@ impl<'tcx> Cx<'tcx> {
             | Res::Def(DefKind::Ctor(_, CtorKind::Fn), _)
             | Res::Def(DefKind::Const, _)
             | Res::Def(DefKind::AssocConst, _) => {
-                self.typeck_results().user_provided_types().get(hir_id).copied()
+                self.typeck_results().user_provided_types().get(hir_id).copied().map(Box::new)
             }
 
             // A unit struct/variant which is used as a value (e.g.,
@@ -767,11 +777,11 @@ impl<'tcx> Cx<'tcx> {
             // this variant -- but with the substitutions given by the
             // user.
             Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => {
-                self.user_substs_applied_to_ty_of_hir_id(hir_id)
+                self.user_substs_applied_to_ty_of_hir_id(hir_id).map(Box::new)
             }
 
             // `Self` is used in expression as a tuple struct constructor or a unit struct constructor
-            Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id),
+            Res::SelfCtor(_) => self.user_substs_applied_to_ty_of_hir_id(hir_id).map(Box::new),
 
             _ => bug!("user_substs_applied_to_res: unexpected res {:?} at {:?}", res, hir_id),
         };
@@ -846,22 +856,22 @@ impl<'tcx> Cx<'tcx> {
 
             Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => {
                 let user_ty = self.user_substs_applied_to_res(expr.hir_id, res);
-                ExprKind::NamedConst { def_id, substs, user_ty: user_ty }
+                ExprKind::NamedConst { def_id, substs, user_ty }
             }
 
             Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id) => {
                 let user_provided_types = self.typeck_results.user_provided_types();
-                let user_provided_type = user_provided_types.get(expr.hir_id).copied();
-                debug!("convert_path_expr: user_provided_type={:?}", user_provided_type);
+                let user_ty = user_provided_types.get(expr.hir_id).copied().map(Box::new);
+                debug!("convert_path_expr: user_ty={:?}", user_ty);
                 let ty = self.typeck_results().node_type(expr.hir_id);
                 match ty.kind() {
                     // A unit struct/variant which is used as a value.
                     // We return a completely different ExprKind here to account for this special case.
-                    ty::Adt(adt_def, substs) => ExprKind::Adt(Box::new(Adt {
+                    ty::Adt(adt_def, substs) => ExprKind::Adt(Box::new(AdtExpr {
                         adt_def: *adt_def,
                         variant_index: adt_def.variant_index_with_ctor_id(def_id),
                         substs,
-                        user_ty: user_provided_type,
+                        user_ty,
                         fields: Box::new([]),
                         base: None,
                     })),
diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml
index baf9735fbc8..385e9ba748f 100644
--- a/compiler/rustc_mir_dataflow/Cargo.toml
+++ b/compiler/rustc_mir_dataflow/Cargo.toml
@@ -13,10 +13,13 @@ smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
 tracing = "0.1"
 rustc_ast = { path = "../rustc_ast" }
 rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
 rustc_graphviz = { path = "../rustc_graphviz" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_index = { path = "../rustc_index" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_middle = { path = "../rustc_middle" }
 rustc_serialize = { path = "../rustc_serialize" }
+rustc_session = { path = "../rustc_session" }
 rustc_target = { path = "../rustc_target" }
 rustc_span = { path = "../rustc_span" }
diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs
new file mode 100644
index 00000000000..cc14257876c
--- /dev/null
+++ b/compiler/rustc_mir_dataflow/src/errors.rs
@@ -0,0 +1,71 @@
+use rustc_macros::SessionDiagnostic;
+use rustc_span::{Span, Symbol};
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::path_must_end_in_filename)]
+pub(crate) struct PathMustEndInFilename {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::unknown_formatter)]
+pub(crate) struct UnknownFormatter {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::duplicate_values_for)]
+pub(crate) struct DuplicateValuesFor {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::requires_an_argument)]
+pub(crate) struct RequiresAnArgument {
+    #[primary_span]
+    pub span: Span,
+    pub name: Symbol,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::stop_after_dataflow_ended_compilation)]
+pub(crate) struct StopAfterDataFlowEndedCompilation;
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::peek_must_be_place_or_ref_place)]
+pub(crate) struct PeekMustBePlaceOrRefPlace {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::peek_must_be_not_temporary)]
+pub(crate) struct PeekMustBeNotTemporary {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::peek_bit_not_set)]
+pub(crate) struct PeekBitNotSet {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::peek_argument_not_a_local)]
+pub(crate) struct PeekArgumentNotALocal {
+    #[primary_span]
+    pub span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(mir_dataflow::peek_argument_untracked)]
+pub(crate) struct PeekArgumentUntracked {
+    #[primary_span]
+    pub span: Span,
+}
diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs
index f374658ceb6..112204c7599 100644
--- a/compiler/rustc_mir_dataflow/src/framework/engine.rs
+++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs
@@ -1,5 +1,8 @@
 //! A solver for dataflow problems.
 
+use crate::errors::{
+    DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter,
+};
 use crate::framework::BitSetExt;
 
 use std::ffi::OsString;
@@ -347,7 +350,7 @@ impl RustcMirAttrs {
                     match path.file_name() {
                         Some(_) => Ok(path),
                         None => {
-                            tcx.sess.span_err(attr.span(), "path must end in a filename");
+                            tcx.sess.emit_err(PathMustEndInFilename { span: attr.span() });
                             Err(())
                         }
                     }
@@ -356,7 +359,7 @@ impl RustcMirAttrs {
                 Self::set_field(&mut ret.formatter, tcx, &attr, |s| match s {
                     sym::gen_kill | sym::two_phase => Ok(s),
                     _ => {
-                        tcx.sess.span_err(attr.span(), "unknown formatter");
+                        tcx.sess.emit_err(UnknownFormatter { span: attr.span() });
                         Err(())
                     }
                 })
@@ -377,8 +380,7 @@ impl RustcMirAttrs {
         mapper: impl FnOnce(Symbol) -> Result<T, ()>,
     ) -> Result<(), ()> {
         if field.is_some() {
-            tcx.sess
-                .span_err(attr.span(), &format!("duplicate values for `{}`", attr.name_or_empty()));
+            tcx.sess.emit_err(DuplicateValuesFor { span: attr.span(), name: attr.name_or_empty() });
 
             return Err(());
         }
@@ -387,8 +389,7 @@ impl RustcMirAttrs {
             *field = Some(mapper(s)?);
             Ok(())
         } else {
-            tcx.sess
-                .span_err(attr.span(), &format!("`{}` requires an argument", attr.name_or_empty()));
+            tcx.sess.emit_err(RequiresAnArgument { span: attr.span(), name: attr.name_or_empty() });
             Err(())
         }
     }
diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
index 21132eb991f..483c1e274aa 100644
--- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs
+++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs
@@ -23,12 +23,6 @@ use crate::{Analysis, AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKi
 /// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
 pub struct MaybeLiveLocals;
 
-impl MaybeLiveLocals {
-    fn transfer_function<'a, T>(&self, trans: &'a mut T) -> TransferFunction<'a, T> {
-        TransferFunction(trans)
-    }
-}
-
 impl<'tcx> AnalysisDomain<'tcx> for MaybeLiveLocals {
     type Domain = ChunkedBitSet<Local>;
     type Direction = Backward;
@@ -54,7 +48,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
         statement: &mir::Statement<'tcx>,
         location: Location,
     ) {
-        self.transfer_function(trans).visit_statement(statement, location);
+        TransferFunction(trans).visit_statement(statement, location);
     }
 
     fn terminator_effect(
@@ -63,7 +57,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
         terminator: &mir::Terminator<'tcx>,
         location: Location,
     ) {
-        self.transfer_function(trans).visit_terminator(terminator, location);
+        TransferFunction(trans).visit_terminator(terminator, location);
     }
 
     fn call_return_effect(
@@ -85,9 +79,11 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeLiveLocals {
         _resume_block: mir::BasicBlock,
         resume_place: mir::Place<'tcx>,
     ) {
-        if let Some(local) = resume_place.as_local() {
-            trans.kill(local);
-        }
+        YieldResumeEffect(trans).visit_place(
+            &resume_place,
+            PlaceContext::MutatingUse(MutatingUseContext::Yield),
+            Location::START,
+        )
     }
 }
 
@@ -98,28 +94,51 @@ where
     T: GenKill<Local>,
 {
     fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
-        let local = place.local;
-
-        // We purposefully do not call `super_place` here to avoid calling `visit_local` for this
-        // place with one of the `Projection` variants of `PlaceContext`.
-        self.visit_projection(place.as_ref(), context, location);
+        if let PlaceContext::MutatingUse(MutatingUseContext::Yield) = context {
+            // The resume place is evaluated and assigned to only after generator resumes, so its
+            // effect is handled separately in `yield_resume_effect`.
+            return;
+        }
 
         match DefUse::for_place(*place, context) {
-            Some(DefUse::Def) => self.0.kill(local),
-            Some(DefUse::Use) => self.0.gen(local),
+            Some(DefUse::Def) => {
+                if let PlaceContext::MutatingUse(
+                    MutatingUseContext::Call | MutatingUseContext::AsmOutput,
+                ) = context
+                {
+                    // For the associated terminators, this is only a `Def` when the terminator returns
+                    // "successfully." As such, we handle this case separately in `call_return_effect`
+                    // above. However, if the place looks like `*_5`, this is still unconditionally a use of
+                    // `_5`.
+                } else {
+                    self.0.kill(place.local);
+                }
+            }
+            Some(DefUse::Use) => self.0.gen(place.local),
             None => {}
         }
+
+        self.visit_projection(place.as_ref(), context, location);
     }
 
     fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
-        // Because we do not call `super_place` above, `visit_local` is only called for locals that
-        // do not appear as part of  a `Place` in the MIR. This handles cases like the implicit use
-        // of the return place in a `Return` terminator or the index in an `Index` projection.
-        match DefUse::for_place(local.into(), context) {
-            Some(DefUse::Def) => self.0.kill(local),
-            Some(DefUse::Use) => self.0.gen(local),
-            None => {}
-        }
+        DefUse::apply(self.0, local.into(), context);
+    }
+}
+
+struct YieldResumeEffect<'a, T>(&'a mut T);
+
+impl<'tcx, T> Visitor<'tcx> for YieldResumeEffect<'_, T>
+where
+    T: GenKill<Local>,
+{
+    fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
+        DefUse::apply(self.0, *place, context);
+        self.visit_projection(place.as_ref(), context, location);
+    }
+
+    fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
+        DefUse::apply(self.0, local.into(), context);
     }
 }
 
@@ -130,11 +149,25 @@ enum DefUse {
 }
 
 impl DefUse {
+    fn apply<'tcx>(trans: &mut impl GenKill<Local>, place: Place<'tcx>, context: PlaceContext) {
+        match DefUse::for_place(place, context) {
+            Some(DefUse::Def) => trans.kill(place.local),
+            Some(DefUse::Use) => trans.gen(place.local),
+            None => {}
+        }
+    }
+
     fn for_place<'tcx>(place: Place<'tcx>, context: PlaceContext) -> Option<DefUse> {
         match context {
             PlaceContext::NonUse(_) => None,
 
-            PlaceContext::MutatingUse(MutatingUseContext::Store | MutatingUseContext::Deinit) => {
+            PlaceContext::MutatingUse(
+                MutatingUseContext::Call
+                | MutatingUseContext::Yield
+                | MutatingUseContext::AsmOutput
+                | MutatingUseContext::Store
+                | MutatingUseContext::Deinit,
+            ) => {
                 if place.is_indirect() {
                     // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a
                     // use.
@@ -152,16 +185,6 @@ impl DefUse {
                 place.is_indirect().then_some(DefUse::Use)
             }
 
-            // For the associated terminators, this is only a `Def` when the terminator returns
-            // "successfully." As such, we handle this case separately in `call_return_effect`
-            // above. However, if the place looks like `*_5`, this is still unconditionally a use of
-            // `_5`.
-            PlaceContext::MutatingUse(
-                MutatingUseContext::Call
-                | MutatingUseContext::Yield
-                | MutatingUseContext::AsmOutput,
-            ) => place.is_indirect().then_some(DefUse::Use),
-
             // All other contexts are uses...
             PlaceContext::MutatingUse(
                 MutatingUseContext::AddressOf
@@ -290,8 +313,10 @@ impl<'a, 'tcx> Analysis<'tcx> for MaybeTransitiveLiveLocals<'a> {
         _resume_block: mir::BasicBlock,
         resume_place: mir::Place<'tcx>,
     ) {
-        if let Some(local) = resume_place.as_local() {
-            trans.remove(local);
-        }
+        YieldResumeEffect(trans).visit_place(
+            &resume_place,
+            PlaceContext::MutatingUse(MutatingUseContext::Yield),
+            Location::START,
+        )
     }
 }
diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs
index 5793a286bd0..62b712f7b8d 100644
--- a/compiler/rustc_mir_dataflow/src/lib.rs
+++ b/compiler/rustc_mir_dataflow/src/lib.rs
@@ -7,6 +7,8 @@
 #![feature(stmt_expr_attributes)]
 #![feature(trusted_step)]
 #![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate tracing;
@@ -33,6 +35,7 @@ use self::move_paths::MoveData;
 
 pub mod drop_flag_effects;
 pub mod elaborate_drops;
+mod errors;
 mod framework;
 pub mod impls;
 pub mod move_paths;
diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
index f2471f37a52..5fb7cb6584b 100644
--- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs
+++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs
@@ -6,6 +6,10 @@ use rustc_middle::mir::MirPass;
 use rustc_middle::mir::{self, Body, Local, Location};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 
+use crate::errors::{
+    PeekArgumentNotALocal, PeekArgumentUntracked, PeekBitNotSet, PeekMustBeNotTemporary,
+    PeekMustBePlaceOrRefPlace, StopAfterDataFlowEndedCompilation,
+};
 use crate::framework::BitSetExt;
 use crate::impls::{
     DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPlaces,
@@ -64,7 +68,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
         }
 
         if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() {
-            tcx.sess.fatal("stop_after_dataflow ended compilation");
+            tcx.sess.emit_fatal(StopAfterDataFlowEndedCompilation);
         }
     }
 }
@@ -133,9 +137,7 @@ pub fn sanity_check_via_rustc_peek<'tcx, A>(
             }
 
             _ => {
-                let msg = "rustc_peek: argument expression \
-                           must be either `place` or `&place`";
-                tcx.sess.span_err(call.span, msg);
+                tcx.sess.emit_err(PeekMustBePlaceOrRefPlace { span: call.span });
             }
         }
     }
@@ -204,18 +206,12 @@ impl PeekCall {
                         if let Some(local) = place.as_local() {
                             local
                         } else {
-                            tcx.sess.diagnostic().span_err(
-                                span,
-                                "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
-                            );
+                            tcx.sess.emit_err(PeekMustBeNotTemporary { span });
                             return None;
                         }
                     }
                     _ => {
-                        tcx.sess.diagnostic().span_err(
-                            span,
-                            "dataflow::sanity_check cannot feed a non-temp to rustc_peek.",
-                        );
+                        tcx.sess.emit_err(PeekMustBeNotTemporary { span });
                         return None;
                     }
                 };
@@ -255,12 +251,12 @@ where
                 let bit_state = flow_state.contains(peek_mpi);
                 debug!("rustc_peek({:?} = &{:?}) bit_state: {}", call.arg, place, bit_state);
                 if !bit_state {
-                    tcx.sess.span_err(call.span, "rustc_peek: bit not set");
+                    tcx.sess.emit_err(PeekBitNotSet { span: call.span });
                 }
             }
 
             LookupResult::Parent(..) => {
-                tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
+                tcx.sess.emit_err(PeekArgumentUntracked { span: call.span });
             }
         }
     }
@@ -276,12 +272,12 @@ impl<'tcx> RustcPeekAt<'tcx> for MaybeLiveLocals {
     ) {
         info!(?place, "peek_at");
         let Some(local) = place.as_local() else {
-            tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
+            tcx.sess.emit_err(PeekArgumentNotALocal { span: call.span });
             return;
         };
 
         if !flow_state.contains(local) {
-            tcx.sess.span_err(call.span, "rustc_peek: bit not set");
+            tcx.sess.emit_err(PeekBitNotSet { span: call.span });
         }
     }
 }
diff --git a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
index e04094153df..294af2455d0 100644
--- a/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
+++ b/compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
@@ -107,27 +107,8 @@ impl<'tcx> MirPass<'tcx> for ElaborateBoxDerefs {
             let mut visitor =
                 ElaborateBoxDerefVisitor { tcx, unique_did, nonnull_did, local_decls, patch };
 
-            for (block, BasicBlockData { statements, terminator, .. }) in
-                body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut()
-            {
-                let mut index = 0;
-                for statement in statements {
-                    let location = Location { block, statement_index: index };
-                    visitor.visit_statement(statement, location);
-                    index += 1;
-                }
-
-                let location = Location { block, statement_index: index };
-                match terminator {
-                    // yielding into a box is handled when lowering generators
-                    Some(Terminator { kind: TerminatorKind::Yield { value, .. }, .. }) => {
-                        visitor.visit_operand(value, location);
-                    }
-                    Some(terminator) => {
-                        visitor.visit_terminator(terminator, location);
-                    }
-                    None => {}
-                }
+            for (block, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
+                visitor.visit_basic_block_data(block, data);
             }
 
             visitor.patch.apply(body);
diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs
index 91ecf387922..5b0d9900c0f 100644
--- a/compiler/rustc_mir_transform/src/generator.rs
+++ b/compiler/rustc_mir_transform/src/generator.rs
@@ -1182,8 +1182,6 @@ fn create_cases<'tcx>(
     transform: &TransformVisitor<'tcx>,
     operation: Operation,
 ) -> Vec<(usize, BasicBlock)> {
-    let tcx = transform.tcx;
-
     let source_info = SourceInfo::outermost(body.span);
 
     transform
@@ -1216,85 +1214,13 @@ fn create_cases<'tcx>(
                 if operation == Operation::Resume {
                     // Move the resume argument to the destination place of the `Yield` terminator
                     let resume_arg = Local::new(2); // 0 = return, 1 = self
-
-                    // handle `box yield` properly
-                    let box_place = if let [projection @ .., ProjectionElem::Deref] =
-                        &**point.resume_arg.projection
-                    {
-                        let box_place =
-                            Place::from(point.resume_arg.local).project_deeper(projection, tcx);
-
-                        let box_ty = box_place.ty(&body.local_decls, tcx).ty;
-
-                        if box_ty.is_box() { Some((box_place, box_ty)) } else { None }
-                    } else {
-                        None
-                    };
-
-                    if let Some((box_place, box_ty)) = box_place {
-                        let unique_did = box_ty
-                            .ty_adt_def()
-                            .expect("expected Box to be an Adt")
-                            .non_enum_variant()
-                            .fields[0]
-                            .did;
-
-                        let Some(nonnull_def) = tcx.type_of(unique_did).ty_adt_def() else {
-                            span_bug!(tcx.def_span(unique_did), "expected Box to contain Unique")
-                        };
-
-                        let nonnull_did = nonnull_def.non_enum_variant().fields[0].did;
-
-                        let (unique_ty, nonnull_ty, ptr_ty) =
-                            crate::elaborate_box_derefs::build_ptr_tys(
-                                tcx,
-                                box_ty.boxed_ty(),
-                                unique_did,
-                                nonnull_did,
-                            );
-
-                        let ptr_local = body.local_decls.push(LocalDecl::new(ptr_ty, body.span));
-
-                        statements.push(Statement {
-                            source_info,
-                            kind: StatementKind::StorageLive(ptr_local),
-                        });
-
-                        statements.push(Statement {
-                            source_info,
-                            kind: StatementKind::Assign(Box::new((
-                                Place::from(ptr_local),
-                                Rvalue::Use(Operand::Copy(box_place.project_deeper(
-                                    &crate::elaborate_box_derefs::build_projection(
-                                        unique_ty, nonnull_ty, ptr_ty,
-                                    ),
-                                    tcx,
-                                ))),
-                            ))),
-                        });
-
-                        statements.push(Statement {
-                            source_info,
-                            kind: StatementKind::Assign(Box::new((
-                                Place::from(ptr_local)
-                                    .project_deeper(&[ProjectionElem::Deref], tcx),
-                                Rvalue::Use(Operand::Move(resume_arg.into())),
-                            ))),
-                        });
-
-                        statements.push(Statement {
-                            source_info,
-                            kind: StatementKind::StorageDead(ptr_local),
-                        });
-                    } else {
-                        statements.push(Statement {
-                            source_info,
-                            kind: StatementKind::Assign(Box::new((
-                                point.resume_arg,
-                                Rvalue::Use(Operand::Move(resume_arg.into())),
-                            ))),
-                        });
-                    }
+                    statements.push(Statement {
+                        source_info,
+                        kind: StatementKind::Assign(Box::new((
+                            point.resume_arg,
+                            Rvalue::Use(Operand::Move(resume_arg.into())),
+                        ))),
+                    });
                 }
 
                 // Then jump to the real target
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index b545b979391..b08d2b376a6 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -250,13 +250,30 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                 .map_or_else(String::new, |res| format!("{} ", res.descr()));
                 (mod_prefix, format!("`{}`", Segment::names_to_string(mod_path)), None)
             };
+
+            let (fallback_label, suggestion) = if path_str == "async"
+                && expected.starts_with("struct")
+            {
+                ("`async` blocks are only allowed in Rust 2018 or later".to_string(), suggestion)
+            } else {
+                // check if we are in situation of typo like `True` instead of `true`.
+                let override_suggestion =
+                    if ["true", "false"].contains(&item_str.to_string().to_lowercase().as_str()) {
+                        let item_typo = item_str.to_string().to_lowercase();
+                        Some((
+                            item_span,
+                            "you may want to use a bool value instead",
+                            format!("{}", item_typo),
+                        ))
+                    } else {
+                        suggestion
+                    };
+                (format!("not found in {mod_str}"), override_suggestion)
+            };
+
             BaseError {
                 msg: format!("cannot find {expected} `{item_str}` in {mod_prefix}{mod_str}"),
-                fallback_label: if path_str == "async" && expected.starts_with("struct") {
-                    "`async` blocks are only allowed in Rust 2018 or later".to_string()
-                } else {
-                    format!("not found in {mod_str}")
-                },
+                fallback_label,
                 span: item_span,
                 span_label: None,
                 could_be_expr: false,
diff --git a/compiler/rustc_serialize/src/lib.rs b/compiler/rustc_serialize/src/lib.rs
index 079d44bac68..91f4cfaf5ac 100644
--- a/compiler/rustc_serialize/src/lib.rs
+++ b/compiler/rustc_serialize/src/lib.rs
@@ -16,6 +16,7 @@ Core encoding and decoding interfaces.
 #![feature(maybe_uninit_slice)]
 #![feature(let_else)]
 #![feature(new_uninit)]
+#![feature(allocator_api)]
 #![cfg_attr(test, feature(test))]
 #![allow(rustc::internal)]
 #![deny(rustc::untranslatable_diagnostic)]
diff --git a/compiler/rustc_serialize/src/serialize.rs b/compiler/rustc_serialize/src/serialize.rs
index 9bd5550038f..751b209f11a 100644
--- a/compiler/rustc_serialize/src/serialize.rs
+++ b/compiler/rustc_serialize/src/serialize.rs
@@ -4,6 +4,7 @@
 Core encoding and decoding interfaces.
 */
 
+use std::alloc::Allocator;
 use std::borrow::Cow;
 use std::cell::{Cell, RefCell};
 use std::marker::PhantomData;
@@ -229,9 +230,9 @@ impl<D: Decoder, T> Decodable<D> for PhantomData<T> {
     }
 }
 
-impl<D: Decoder, T: Decodable<D>> Decodable<D> for Box<[T]> {
-    fn decode(d: &mut D) -> Box<[T]> {
-        let v: Vec<T> = Decodable::decode(d);
+impl<D: Decoder, A: Allocator + Default, T: Decodable<D>> Decodable<D> for Box<[T], A> {
+    fn decode(d: &mut D) -> Box<[T], A> {
+        let v: Vec<T, A> = Decodable::decode(d);
         v.into_boxed_slice()
     }
 }
@@ -264,12 +265,13 @@ impl<S: Encoder, T: Encodable<S>> Encodable<S> for Vec<T> {
     }
 }
 
-impl<D: Decoder, T: Decodable<D>> Decodable<D> for Vec<T> {
-    default fn decode(d: &mut D) -> Vec<T> {
+impl<D: Decoder, T: Decodable<D>, A: Allocator + Default> Decodable<D> for Vec<T, A> {
+    default fn decode(d: &mut D) -> Vec<T, A> {
         let len = d.read_usize();
+        let allocator = A::default();
         // SAFETY: we set the capacity in advance, only write elements, and
         // only set the length at the end once the writing has succeeded.
-        let mut vec = Vec::with_capacity(len);
+        let mut vec = Vec::with_capacity_in(len, allocator);
         unsafe {
             let ptr: *mut T = vec.as_mut_ptr();
             for i in 0..len {
@@ -457,13 +459,15 @@ impl<D: Decoder, T: Decodable<D>> Decodable<D> for Arc<T> {
     }
 }
 
-impl<S: Encoder, T: ?Sized + Encodable<S>> Encodable<S> for Box<T> {
+impl<S: Encoder, T: ?Sized + Encodable<S>, A: Allocator + Default> Encodable<S> for Box<T, A> {
     fn encode(&self, s: &mut S) {
-        (**self).encode(s);
+        (**self).encode(s)
     }
 }
-impl<D: Decoder, T: Decodable<D>> Decodable<D> for Box<T> {
-    fn decode(d: &mut D) -> Box<T> {
-        Box::new(Decodable::decode(d))
+
+impl<D: Decoder, A: Allocator + Default, T: Decodable<D>> Decodable<D> for Box<T, A> {
+    fn decode(d: &mut D) -> Box<T, A> {
+        let allocator = A::default();
+        Box::new_in(Decodable::decode(d), allocator)
     }
 }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 68519c8fa82..162fc9aa0a6 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -2423,13 +2423,6 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
 
     let pretty = parse_pretty(&unstable_opts, error_format);
 
-    if !unstable_opts.unstable_options
-        && !target_triple.triple().contains("apple")
-        && cg.split_debuginfo.is_some()
-    {
-        early_error(error_format, "`-Csplit-debuginfo` is unstable on this platform");
-    }
-
     // Try to find a directory containing the Rust `src`, for more details see
     // the doc comment on the `real_rust_source_base_dir` field.
     let tmp_buf;
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 4972ae2014d..ec5e5170d35 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -20,8 +20,8 @@ use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
 use rustc_errors::{
-    fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage, EmissionGuarantee,
-    ErrorGuaranteed, FluentBundle, LazyFallbackBundle, MultiSpan,
+    error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
+    EmissionGuarantee, ErrorGuaranteed, FluentBundle, LazyFallbackBundle, MultiSpan,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -31,7 +31,7 @@ use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
 use rustc_target::asm::InlineAsmArch;
 use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
 use rustc_target::spec::{
-    SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
+    DebuginfoKind, SanitizerSet, SplitDebuginfo, StackProtector, Target, TargetTriple, TlsModel,
 };
 
 use std::cell::{self, RefCell};
@@ -467,6 +467,9 @@ impl Session {
         feature: Symbol,
     ) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
         let mut err = self.parse_sess.create_err(err);
+        if err.code.is_none() {
+            err.code = std::option::Option::Some(error_code!(E0658));
+        }
         add_feature_diagnostics(&mut err, &self.parse_sess, feature);
         err
     }
@@ -670,8 +673,9 @@ impl Session {
             )
     }
 
+    /// Returns `true` if the target can use the current split debuginfo configuration.
     pub fn target_can_use_split_dwarf(&self) -> bool {
-        !self.target.is_like_windows && !self.target.is_like_osx
+        self.target.debuginfo_kind == DebuginfoKind::Dwarf
     }
 
     pub fn generate_proc_macro_decls_symbol(&self, stable_crate_id: StableCrateId) -> String {
@@ -1552,6 +1556,15 @@ fn validate_commandline_args_with_session_available(sess: &Session) {
             sess.err(&format!("requested DWARF version {} is greater than 5", dwarf_version));
         }
     }
+
+    if !sess.target.options.supported_split_debuginfo.contains(&sess.split_debuginfo())
+        && !sess.opts.unstable_opts.unstable_options
+    {
+        sess.err(&format!(
+            "`-Csplit-debuginfo={}` is unstable on this platform",
+            sess.split_debuginfo()
+        ));
+    }
 }
 
 /// Holds data on the current incremental compilation session, if there is one.
diff --git a/compiler/rustc_target/src/spec/apple_base.rs b/compiler/rustc_target/src/spec/apple_base.rs
index db38ff50c78..9bbee88a894 100644
--- a/compiler/rustc_target/src/spec/apple_base.rs
+++ b/compiler/rustc_target/src/spec/apple_base.rs
@@ -1,6 +1,6 @@
 use std::{borrow::Cow, env};
 
-use crate::spec::{cvs, FramePointer, SplitDebuginfo, TargetOptions};
+use crate::spec::{cvs, DebuginfoKind, FramePointer, SplitDebuginfo, TargetOptions};
 use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor};
 
 fn pre_link_args(os: &'static str, arch: &'static str, abi: &'static str) -> LinkArgs {
@@ -76,9 +76,15 @@ pub fn opts(os: &'static str, arch: &'static str, abi: &'static str) -> TargetOp
         eh_frame_header: false,
         lld_flavor: LldFlavor::Ld64,
 
+        debuginfo_kind: DebuginfoKind::DwarfDsym,
         // The historical default for macOS targets is to run `dsymutil` which
         // generates a packed version of debuginfo split from the main file.
         split_debuginfo: SplitDebuginfo::Packed,
+        supported_split_debuginfo: Cow::Borrowed(&[
+            SplitDebuginfo::Packed,
+            SplitDebuginfo::Unpacked,
+            SplitDebuginfo::Off,
+        ]),
 
         // This environment variable is pretty magical but is intended for
         // producing deterministic builds. This was first discovered to be used
diff --git a/compiler/rustc_target/src/spec/linux_base.rs b/compiler/rustc_target/src/spec/linux_base.rs
index f4fce3b4050..df8e848124a 100644
--- a/compiler/rustc_target/src/spec/linux_base.rs
+++ b/compiler/rustc_target/src/spec/linux_base.rs
@@ -1,4 +1,5 @@
-use crate::spec::{cvs, RelroLevel, TargetOptions};
+use crate::spec::{cvs, RelroLevel, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
 
 pub fn opts() -> TargetOptions {
     TargetOptions {
@@ -10,6 +11,11 @@ pub fn opts() -> TargetOptions {
         relro_level: RelroLevel::Full,
         has_thread_local: true,
         crt_static_respected: true,
+        supported_split_debuginfo: Cow::Borrowed(&[
+            SplitDebuginfo::Packed,
+            SplitDebuginfo::Unpacked,
+            SplitDebuginfo::Off,
+        ]),
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index cf6cb75d49a..8d00129b1b1 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -468,7 +468,57 @@ impl fmt::Display for LinkOutputKind {
 
 pub type LinkArgs = BTreeMap<LinkerFlavor, Vec<StaticCow<str>>>;
 
-#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq)]
+/// Which kind of debuginfo does the target use?
+///
+/// Useful in determining whether a target supports Split DWARF (a target with
+/// `DebuginfoKind::Dwarf` and supporting `SplitDebuginfo::Unpacked` for example).
+#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
+pub enum DebuginfoKind {
+    /// DWARF debuginfo (such as that used on `x86_64_unknown_linux_gnu`).
+    #[default]
+    Dwarf,
+    /// DWARF debuginfo in dSYM files (such as on Apple platforms).
+    DwarfDsym,
+    /// Program database files (such as on Windows).
+    Pdb,
+}
+
+impl DebuginfoKind {
+    fn as_str(&self) -> &'static str {
+        match self {
+            DebuginfoKind::Dwarf => "dwarf",
+            DebuginfoKind::DwarfDsym => "dwarf-dsym",
+            DebuginfoKind::Pdb => "pdb",
+        }
+    }
+}
+
+impl FromStr for DebuginfoKind {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, ()> {
+        Ok(match s {
+            "dwarf" => DebuginfoKind::Dwarf,
+            "dwarf-dsym" => DebuginfoKind::DwarfDsym,
+            "pdb" => DebuginfoKind::Pdb,
+            _ => return Err(()),
+        })
+    }
+}
+
+impl ToJson for DebuginfoKind {
+    fn to_json(&self) -> Json {
+        self.as_str().to_json()
+    }
+}
+
+impl fmt::Display for DebuginfoKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(self.as_str())
+    }
+}
+
+#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
 pub enum SplitDebuginfo {
     /// Split debug-information is disabled, meaning that on supported platforms
     /// you can find all debug information in the executable itself. This is
@@ -476,7 +526,8 @@ pub enum SplitDebuginfo {
     ///
     /// * Windows - not supported
     /// * macOS - don't run `dsymutil`
-    /// * ELF - `.dwarf_*` sections
+    /// * ELF - `.debug_*` sections
+    #[default]
     Off,
 
     /// Split debug-information can be found in a "packed" location separate
@@ -484,7 +535,7 @@ pub enum SplitDebuginfo {
     ///
     /// * Windows - `*.pdb`
     /// * macOS - `*.dSYM` (run `dsymutil`)
-    /// * ELF - `*.dwp` (run `rust-llvm-dwp`)
+    /// * ELF - `*.dwp` (run `thorin`)
     Packed,
 
     /// Split debug-information can be found in individual object files on the
@@ -509,7 +560,7 @@ impl SplitDebuginfo {
 impl FromStr for SplitDebuginfo {
     type Err = ();
 
-    fn from_str(s: &str) -> Result<SplitDebuginfo, ()> {
+    fn from_str(s: &str) -> Result<Self, ()> {
         Ok(match s {
             "off" => SplitDebuginfo::Off,
             "unpacked" => SplitDebuginfo::Unpacked,
@@ -1436,9 +1487,13 @@ pub struct TargetOptions {
     /// thumb and arm interworking.
     pub has_thumb_interworking: bool,
 
+    /// Which kind of debuginfo is used by this target?
+    pub debuginfo_kind: DebuginfoKind,
     /// How to handle split debug information, if at all. Specifying `None` has
     /// target-specific meaning.
     pub split_debuginfo: SplitDebuginfo,
+    /// Which kinds of split debuginfo are supported by the target?
+    pub supported_split_debuginfo: StaticCow<[SplitDebuginfo]>,
 
     /// The sanitizers supported by this target
     ///
@@ -1596,7 +1651,10 @@ impl Default for TargetOptions {
             use_ctors_section: false,
             eh_frame_header: true,
             has_thumb_interworking: false,
-            split_debuginfo: SplitDebuginfo::Off,
+            debuginfo_kind: Default::default(),
+            split_debuginfo: Default::default(),
+            // `Off` is supported by default, but targets can remove this manually, e.g. Windows.
+            supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
             supported_sanitizers: SanitizerSet::empty(),
             default_adjusted_cabi: None,
             c_enum_min_bits: 32,
@@ -1869,6 +1927,19 @@ impl Target {
                     Some(Ok(()))
                 })).unwrap_or(Ok(()))
             } );
+            ($key_name:ident, DebuginfoKind) => ( {
+                let name = (stringify!($key_name)).replace("_", "-");
+                obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
+                    match s.parse::<DebuginfoKind>() {
+                        Ok(level) => base.$key_name = level,
+                        _ => return Some(Err(
+                            format!("'{s}' is not a valid value for debuginfo-kind. Use 'dwarf', \
+                                  'dwarf-dsym' or 'pdb'.")
+                        )),
+                    }
+                    Some(Ok(()))
+                })).unwrap_or(Ok(()))
+            } );
             ($key_name:ident, SplitDebuginfo) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 obj.remove(&name).and_then(|o| o.as_str().and_then(|s| {
@@ -1905,6 +1976,25 @@ impl Target {
                     }
                 }
             } );
+            ($key_name:ident, falliable_list) => ( {
+                let name = (stringify!($key_name)).replace("_", "-");
+                obj.remove(&name).and_then(|j| {
+                    if let Some(v) = j.as_array() {
+                        match v.iter().map(|a| FromStr::from_str(a.as_str().unwrap())).collect() {
+                            Ok(l) => { base.$key_name = l },
+                            // FIXME: `falliable_list` can't re-use the `key!` macro for list
+                            // elements and the error messages from that macro, so it has a bad
+                            // generic message instead
+                            Err(_) => return Some(Err(
+                                format!("`{:?}` is not a valid value for `{}`", j, name)
+                            )),
+                        }
+                    } else {
+                        incorrect_type.push(name)
+                    }
+                    Some(Ok(()))
+                }).unwrap_or(Ok(()))
+            } );
             ($key_name:ident, optional) => ( {
                 let name = (stringify!($key_name)).replace("_", "-");
                 if let Some(o) = obj.remove(&name) {
@@ -2191,7 +2281,9 @@ impl Target {
         key!(use_ctors_section, bool);
         key!(eh_frame_header, bool);
         key!(has_thumb_interworking, bool);
+        key!(debuginfo_kind, DebuginfoKind)?;
         key!(split_debuginfo, SplitDebuginfo)?;
+        key!(supported_split_debuginfo, falliable_list)?;
         key!(supported_sanitizers, SanitizerSet)?;
         key!(default_adjusted_cabi, Option<Abi>)?;
         key!(c_enum_min_bits, u64);
@@ -2435,7 +2527,9 @@ impl ToJson for Target {
         target_option_val!(use_ctors_section);
         target_option_val!(eh_frame_header);
         target_option_val!(has_thumb_interworking);
+        target_option_val!(debuginfo_kind);
         target_option_val!(split_debuginfo);
+        target_option_val!(supported_split_debuginfo);
         target_option_val!(supported_sanitizers);
         target_option_val!(c_enum_min_bits);
         target_option_val!(generate_arange_section);
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
index edb30b72bf6..ec9609a2b26 100644
--- a/compiler/rustc_target/src/spec/msvc_base.rs
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -1,4 +1,5 @@
 use crate::spec::{LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
 
 pub fn opts() -> TargetOptions {
     // Suppress the verbose logo and authorship debugging output, which would needlessly
@@ -18,6 +19,7 @@ pub fn opts() -> TargetOptions {
         // Currently this is the only supported method of debuginfo on MSVC
         // where `*.pdb` files show up next to the final artifact.
         split_debuginfo: SplitDebuginfo::Packed,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Packed]),
 
         ..Default::default()
     }
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
index 0107f7a52c6..81d44a963f1 100644
--- a/compiler/rustc_target/src/spec/windows_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -1,5 +1,6 @@
 use crate::spec::crt_objects::{self, LinkSelfContainedDefault};
-use crate::spec::{cvs, LinkerFlavor, TargetOptions};
+use crate::spec::{cvs, DebuginfoKind, LinkerFlavor, SplitDebuginfo, TargetOptions};
+use std::borrow::Cow;
 
 pub fn opts() -> TargetOptions {
     let mut pre_link_args = TargetOptions::link_args(
@@ -86,6 +87,10 @@ pub fn opts() -> TargetOptions {
         emit_debug_gdb_scripts: false,
         requires_uwtable: true,
         eh_frame_header: false,
+        // FIXME(davidtwco): Support Split DWARF on Windows GNU - may require LLVM changes to
+        // output DWO, despite using DWARF, doesn't use ELF..
+        debuginfo_kind: DebuginfoKind::Pdb,
+        supported_split_debuginfo: Cow::Borrowed(&[SplitDebuginfo::Off]),
         ..Default::default()
     }
 }
diff --git a/compiler/rustc_target/src/spec/windows_msvc_base.rs b/compiler/rustc_target/src/spec/windows_msvc_base.rs
index 21062c337d8..67282c19541 100644
--- a/compiler/rustc_target/src/spec/windows_msvc_base.rs
+++ b/compiler/rustc_target/src/spec/windows_msvc_base.rs
@@ -1,4 +1,4 @@
-use crate::spec::{cvs, TargetOptions};
+use crate::spec::{cvs, DebuginfoKind, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     let base = super::msvc_base::opts();
@@ -28,6 +28,7 @@ pub fn opts() -> TargetOptions {
         // not ever be possible for us to pass this flag.
         no_default_libraries: false,
         has_thread_local: true,
+        debuginfo_kind: DebuginfoKind::Pdb,
 
         ..base
     }
diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs
index 904c81f9215..e11ea7751aa 100644
--- a/compiler/rustc_trait_selection/src/traits/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/mod.rs
@@ -579,9 +579,16 @@ fn is_impossible_method<'tcx>(
     });
 
     tcx.infer_ctxt().ignoring_regions().enter(|ref infcx| {
-        let mut fulfill_ctxt = <dyn TraitEngine<'_>>::new(tcx);
-        fulfill_ctxt.register_predicate_obligations(infcx, predicates_for_trait);
-        !fulfill_ctxt.select_all_or_error(infcx).is_empty()
+        for obligation in predicates_for_trait {
+            // Ignore overflow error, to be conservative.
+            if let Ok(result) = infcx.evaluate_obligation(&obligation)
+                && !result.may_apply()
+            {
+                return true;
+            }
+        }
+
+        false
     })
 }
 
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index aeaccffd5f5..444ca6471e2 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -557,7 +557,10 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
                 // For cases like #95134 we would like to catch overflows early
                 // otherwise they slip away away and cause ICE.
                 let recursion_limit = self.tcx().recursion_limit();
-                if !recursion_limit.value_within_limit(self.depth) {
+                if !recursion_limit.value_within_limit(self.depth)
+                    // HACK: Don't overflow when running cargo doc see #100991
+                    && !self.tcx().sess.opts.actually_rustdoc
+                {
                     let obligation = Obligation::with_depth(
                         self.cause.clone(),
                         recursion_limit.0,
diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs
index 70b3ba02b05..93cab7ca533 100644
--- a/compiler/rustc_transmute/src/layout/tree.rs
+++ b/compiler/rustc_transmute/src/layout/tree.rs
@@ -1,4 +1,5 @@
 use super::{Byte, Def, Ref};
+use std::ops::ControlFlow;
 
 #[cfg(test)]
 mod tests;
@@ -86,17 +87,18 @@ where
         F: Fn(D) -> bool,
     {
         match self {
-            Self::Seq(elts) => elts
-                .into_iter()
-                .map(|elt| elt.prune(f))
-                .try_fold(Tree::unit(), |elts, elt| {
+            Self::Seq(elts) => match elts.into_iter().map(|elt| elt.prune(f)).try_fold(
+                Tree::unit(),
+                |elts, elt| {
                     if elt == Tree::uninhabited() {
-                        Err(Tree::uninhabited())
+                        ControlFlow::Break(Tree::uninhabited())
                     } else {
-                        Ok(elts.then(elt))
+                        ControlFlow::Continue(elts.then(elt))
                     }
-                })
-                .into_ok_or_err(),
+                },
+            ) {
+                ControlFlow::Break(node) | ControlFlow::Continue(node) => node,
+            },
             Self::Alt(alts) => alts
                 .into_iter()
                 .map(|alt| alt.prune(f))
diff --git a/compiler/rustc_transmute/src/lib.rs b/compiler/rustc_transmute/src/lib.rs
index 32e6cb9c64f..89b1ce5abe9 100644
--- a/compiler/rustc_transmute/src/lib.rs
+++ b/compiler/rustc_transmute/src/lib.rs
@@ -1,11 +1,4 @@
-#![feature(
-    alloc_layout_extra,
-    control_flow_enum,
-    decl_macro,
-    iterator_try_reduce,
-    never_type,
-    result_into_ok_or_err
-)]
+#![feature(alloc_layout_extra, control_flow_enum, decl_macro, iterator_try_reduce, never_type)]
 #![allow(dead_code, unused_variables)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
index caad2ed4274..52fbd3ae047 100644
--- a/compiler/rustc_ty_utils/Cargo.toml
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -10,6 +10,7 @@ rustc_data_structures = { path = "../rustc_data_structures" }
 rustc_errors = { path = "../rustc_errors" }
 rustc_hir = { path = "../rustc_hir" }
 rustc_infer = { path = "../rustc_infer" }
+rustc_macros = { path = "../rustc_macros" }
 rustc_span = { path = "../rustc_span" }
 rustc_session = { path = "../rustc_session" }
 rustc_target = { path = "../rustc_target" }
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
index 7c2f4db94ff..16c4d429129 100644
--- a/compiler/rustc_ty_utils/src/consts.rs
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -11,6 +11,8 @@ use rustc_target::abi::VariantIdx;
 
 use std::iter;
 
+use crate::errors::{GenericConstantTooComplex, GenericConstantTooComplexSub};
+
 /// Destructures array, ADT or tuple constants into the constants
 /// of their fields.
 pub(crate) fn destructure_const<'tcx>(
@@ -93,26 +95,25 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
         self.body.exprs[self.body_id].span
     }
 
-    fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
-        let reported = self
-            .tcx
-            .sess
-            .struct_span_err(self.root_span(), "overly complex generic constant")
-            .span_label(span, msg)
-            .help("consider moving this anonymous constant into a `const` function")
-            .emit();
+    fn error(&mut self, sub: GenericConstantTooComplexSub) -> Result<!, ErrorGuaranteed> {
+        let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
+            span: self.root_span(),
+            maybe_supported: None,
+            sub,
+        });
 
         Err(reported)
     }
-    fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
-        let reported = self
-            .tcx
-            .sess
-            .struct_span_err(self.root_span(), "overly complex generic constant")
-            .span_label(span, msg)
-            .help("consider moving this anonymous constant into a `const` function")
-            .note("this operation may be supported in the future")
-            .emit();
+
+    fn maybe_supported_error(
+        &mut self,
+        sub: GenericConstantTooComplexSub,
+    ) -> Result<!, ErrorGuaranteed> {
+        let reported = self.tcx.sess.emit_err(GenericConstantTooComplex {
+            span: self.root_span(),
+            maybe_supported: Some(()),
+            sub,
+        });
 
         Err(reported)
     }
@@ -243,22 +244,23 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
             &ExprKind::PlaceTypeAscription { source, .. }
             | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
-            &ExprKind::Literal { lit, neg} => {
+            &ExprKind::Literal { lit, neg } => {
                 let sp = node.span;
-                let constant =
-                    match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
-                        Ok(c) => c,
-                        Err(LitToConstError::Reported) => {
-                            self.tcx.const_error(node.ty)
-                        }
-                        Err(LitToConstError::TypeError) => {
-                            bug!("encountered type error in lit_to_const")
-                        }
-                    };
+                let constant = match self.tcx.at(sp).lit_to_const(LitToConstInput {
+                    lit: &lit.node,
+                    ty: node.ty,
+                    neg,
+                }) {
+                    Ok(c) => c,
+                    Err(LitToConstError::Reported) => self.tcx.const_error(node.ty),
+                    Err(LitToConstError::TypeError) => {
+                        bug!("encountered type error in lit_to_const")
+                    }
+                };
 
                 self.nodes.push(Node::Leaf(constant))
             }
-            &ExprKind::NonHirLiteral { lit , user_ty: _} => {
+            &ExprKind::NonHirLiteral { lit, user_ty: _ } => {
                 let val = ty::ValTree::from_scalar_int(lit);
                 self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
             }
@@ -269,19 +271,17 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
                 let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
 
-                let constant = self.tcx.mk_const(ty::ConstS {
-                                kind: ty::ConstKind::Unevaluated(uneval),
-                                ty: node.ty,
-                            });
+                let constant = self
+                    .tcx
+                    .mk_const(ty::ConstS { kind: ty::ConstKind::Unevaluated(uneval), ty: node.ty });
 
                 self.nodes.push(Node::Leaf(constant))
             }
 
-            ExprKind::ConstParam {param, ..} => {
-                let const_param = self.tcx.mk_const(ty::ConstS {
-                        kind: ty::ConstKind::Param(*param),
-                        ty: node.ty,
-                    });
+            ExprKind::ConstParam { param, .. } => {
+                let const_param = self
+                    .tcx
+                    .mk_const(ty::ConstS { kind: ty::ConstKind::Param(*param), ty: node.ty });
                 self.nodes.push(Node::Leaf(const_param))
             }
 
@@ -311,8 +311,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             //     bar::<{ N + 1 }>();
             // }
             // ```
-            ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
-                self.recurse_build(*e)?
+            ExprKind::Block { block } => {
+                if let thir::Block { stmts: box [], expr: Some(e), .. } = &self.body.blocks[*block]
+                {
+                    self.recurse_build(*e)?
+                } else {
+                    self.maybe_supported_error(GenericConstantTooComplexSub::BlockNotSupported(
+                        node.span,
+                    ))?
+                }
             }
             // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
             // "coercion cast" i.e. using a coercion or is a no-op.
@@ -325,7 +332,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                 let arg = self.recurse_build(source)?;
                 self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
             }
-            ExprKind::Borrow{ arg, ..} => {
+            ExprKind::Borrow { arg, .. } => {
                 let arg_node = &self.body.exprs[*arg];
 
                 // Skip reborrows for now until we allow Deref/Borrow/AddressOf
@@ -334,80 +341,69 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
                 if let ExprKind::Deref { arg } = arg_node.kind {
                     self.recurse_build(arg)?
                 } else {
-                    self.maybe_supported_error(
+                    self.maybe_supported_error(GenericConstantTooComplexSub::BorrowNotSupported(
                         node.span,
-                        "borrowing is not supported in generic constants",
-                    )?
+                    ))?
                 }
             }
             // FIXME(generic_const_exprs): We may want to support these.
-            ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
-                node.span,
-                "dereferencing or taking the address is not supported in generic constants",
-            )?,
-            ExprKind::Repeat { .. } | ExprKind::Array { .. } =>  self.maybe_supported_error(
-                node.span,
-                "array construction is not supported in generic constants",
+            ExprKind::AddressOf { .. } | ExprKind::Deref { .. } => self.maybe_supported_error(
+                GenericConstantTooComplexSub::AddressAndDerefNotSupported(node.span),
             )?,
-            ExprKind::Block { .. } => self.maybe_supported_error(
-                node.span,
-                "blocks are not supported in generic constant",
+            ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error(
+                GenericConstantTooComplexSub::ArrayNotSupported(node.span),
             )?,
             ExprKind::NeverToAny { .. } => self.maybe_supported_error(
-                node.span,
-                "converting nevers to any is not supported in generic constant",
+                GenericConstantTooComplexSub::NeverToAnyNotSupported(node.span),
             )?,
             ExprKind::Tuple { .. } => self.maybe_supported_error(
-                node.span,
-                "tuple construction is not supported in generic constants",
+                GenericConstantTooComplexSub::TupleNotSupported(node.span),
             )?,
             ExprKind::Index { .. } => self.maybe_supported_error(
-                node.span,
-                "indexing is not supported in generic constant",
+                GenericConstantTooComplexSub::IndexNotSupported(node.span),
             )?,
             ExprKind::Field { .. } => self.maybe_supported_error(
-                node.span,
-                "field access is not supported in generic constant",
+                GenericConstantTooComplexSub::FieldNotSupported(node.span),
             )?,
             ExprKind::ConstBlock { .. } => self.maybe_supported_error(
-                node.span,
-                "const blocks are not supported in generic constant",
-            )?,
-            ExprKind::Adt(_) => self.maybe_supported_error(
-                node.span,
-                "struct/enum construction is not supported in generic constants",
+                GenericConstantTooComplexSub::ConstBlockNotSupported(node.span),
             )?,
+            ExprKind::Adt(_) => self
+                .maybe_supported_error(GenericConstantTooComplexSub::AdtNotSupported(node.span))?,
             // dont know if this is correct
-            ExprKind::Pointer { .. } =>
-                self.error(node.span, "pointer casts are not allowed in generic constants")?,
-            ExprKind::Yield { .. } =>
-                self.error(node.span, "generator control flow is not allowed in generic constants")?,
-            ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
-                .error(
-                    node.span,
-                    "loops and loop control flow are not supported in generic constants",
-                )?,
-            ExprKind::Box { .. } =>
-                self.error(node.span, "allocations are not allowed in generic constants")?,
+            ExprKind::Pointer { .. } => {
+                self.error(GenericConstantTooComplexSub::PointerNotSupported(node.span))?
+            }
+            ExprKind::Yield { .. } => {
+                self.error(GenericConstantTooComplexSub::YieldNotSupported(node.span))?
+            }
+            ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => {
+                self.error(GenericConstantTooComplexSub::LoopNotSupported(node.span))?
+            }
+            ExprKind::Box { .. } => {
+                self.error(GenericConstantTooComplexSub::BoxNotSupported(node.span))?
+            }
 
             ExprKind::Unary { .. } => unreachable!(),
             // we handle valid unary/binary ops above
-            ExprKind::Binary { .. } =>
-                self.error(node.span, "unsupported binary operation in generic constants")?,
-            ExprKind::LogicalOp { .. } =>
-                self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
+            ExprKind::Binary { .. } => {
+                self.error(GenericConstantTooComplexSub::BinaryNotSupported(node.span))?
+            }
+            ExprKind::LogicalOp { .. } => {
+                self.error(GenericConstantTooComplexSub::LogicalOpNotSupported(node.span))?
+            }
             ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
-                self.error(node.span, "assignment is not supported in generic constants")?
+                self.error(GenericConstantTooComplexSub::AssignNotSupported(node.span))?
+            }
+            ExprKind::Closure { .. } | ExprKind::Return { .. } => {
+                self.error(GenericConstantTooComplexSub::ClosureAndReturnNotSupported(node.span))?
             }
-            ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
-                node.span,
-                "closures and function keywords are not supported in generic constants",
-            )?,
             // let expressions imply control flow
-            ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
-                self.error(node.span, "control flow is not supported in generic constants")?,
+            ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } => {
+                self.error(GenericConstantTooComplexSub::ControlFlowNotSupported(node.span))?
+            }
             ExprKind::InlineAsm { .. } => {
-                self.error(node.span, "assembly is not supported in generic constants")?
+                self.error(GenericConstantTooComplexSub::InlineAsmNotSupported(node.span))?
             }
 
             // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
@@ -415,7 +411,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
             | ExprKind::UpvarRef { .. }
             | ExprKind::StaticRef { .. }
             | ExprKind::ThreadLocalRef(_) => {
-                self.error(node.span, "unsupported operation in generic constant")?
+                self.error(GenericConstantTooComplexSub::OperationNotSupported(node.span))?
             }
         })
     }
diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs
new file mode 100644
index 00000000000..3a8ef96c991
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/errors.rs
@@ -0,0 +1,69 @@
+//! Errors emitted by ty_utils
+
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+use rustc_middle::ty::Ty;
+use rustc_span::Span;
+
+#[derive(SessionDiagnostic)]
+#[diag(ty_utils::needs_drop_overflow)]
+pub struct NeedsDropOverflow<'tcx> {
+    pub query_ty: Ty<'tcx>,
+}
+
+#[derive(SessionDiagnostic)]
+#[diag(ty_utils::generic_constant_too_complex)]
+#[help]
+pub struct GenericConstantTooComplex {
+    #[primary_span]
+    pub span: Span,
+    #[note(ty_utils::maybe_supported)]
+    pub maybe_supported: Option<()>,
+    #[subdiagnostic]
+    pub sub: GenericConstantTooComplexSub,
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum GenericConstantTooComplexSub {
+    #[label(ty_utils::borrow_not_supported)]
+    BorrowNotSupported(#[primary_span] Span),
+    #[label(ty_utils::address_and_deref_not_supported)]
+    AddressAndDerefNotSupported(#[primary_span] Span),
+    #[label(ty_utils::array_not_supported)]
+    ArrayNotSupported(#[primary_span] Span),
+    #[label(ty_utils::block_not_supported)]
+    BlockNotSupported(#[primary_span] Span),
+    #[label(ty_utils::never_to_any_not_supported)]
+    NeverToAnyNotSupported(#[primary_span] Span),
+    #[label(ty_utils::tuple_not_supported)]
+    TupleNotSupported(#[primary_span] Span),
+    #[label(ty_utils::index_not_supported)]
+    IndexNotSupported(#[primary_span] Span),
+    #[label(ty_utils::field_not_supported)]
+    FieldNotSupported(#[primary_span] Span),
+    #[label(ty_utils::const_block_not_supported)]
+    ConstBlockNotSupported(#[primary_span] Span),
+    #[label(ty_utils::adt_not_supported)]
+    AdtNotSupported(#[primary_span] Span),
+    #[label(ty_utils::pointer_not_supported)]
+    PointerNotSupported(#[primary_span] Span),
+    #[label(ty_utils::yield_not_supported)]
+    YieldNotSupported(#[primary_span] Span),
+    #[label(ty_utils::loop_not_supported)]
+    LoopNotSupported(#[primary_span] Span),
+    #[label(ty_utils::box_not_supported)]
+    BoxNotSupported(#[primary_span] Span),
+    #[label(ty_utils::binary_not_supported)]
+    BinaryNotSupported(#[primary_span] Span),
+    #[label(ty_utils::logical_op_not_supported)]
+    LogicalOpNotSupported(#[primary_span] Span),
+    #[label(ty_utils::assign_not_supported)]
+    AssignNotSupported(#[primary_span] Span),
+    #[label(ty_utils::closure_and_return_not_supported)]
+    ClosureAndReturnNotSupported(#[primary_span] Span),
+    #[label(ty_utils::control_flow_not_supported)]
+    ControlFlowNotSupported(#[primary_span] Span),
+    #[label(ty_utils::inline_asm_not_supported)]
+    InlineAsmNotSupported(#[primary_span] Span),
+    #[label(ty_utils::operation_not_supported)]
+    OperationNotSupported(#[primary_span] Span),
+}
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 55d82693994..6931b15b1ba 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -10,6 +10,8 @@
 #![feature(never_type)]
 #![feature(box_patterns)]
 #![recursion_limit = "256"]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
 
 #[macro_use]
 extern crate rustc_middle;
@@ -21,6 +23,7 @@ use rustc_middle::ty::query::Providers;
 mod assoc;
 mod common_traits;
 mod consts;
+mod errors;
 mod implied_bounds;
 pub mod instance;
 mod needs_drop;
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
index 9ad44d14d61..ab5a3d8ae48 100644
--- a/compiler/rustc_ty_utils/src/needs_drop.rs
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -9,6 +9,8 @@ use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
 use rustc_session::Limit;
 use rustc_span::{sym, DUMMY_SP};
 
+use crate::errors::NeedsDropOverflow;
+
 type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
 
 fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
@@ -90,10 +92,7 @@ where
             if !self.recursion_limit.value_within_limit(level) {
                 // Not having a `Span` isn't great. But there's hopefully some other
                 // recursion limit error as well.
-                tcx.sess.span_err(
-                    DUMMY_SP,
-                    &format!("overflow while checking whether `{}` requires drop", self.query_ty),
-                );
+                tcx.sess.emit_err(NeedsDropOverflow { query_ty: self.query_ty });
                 return Some(Err(AlwaysRequiresDrop));
             }
 
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 44c7c148c75..441a62256de 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -1066,16 +1066,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     // that had unsatisfied trait bounds
                     if unsatisfied_predicates.is_empty() {
                         let def_kind = lev_candidate.kind.as_def_kind();
-                        err.span_suggestion(
-                            span,
-                            &format!(
-                                "there is {} {} with a similar name",
-                                def_kind.article(),
-                                def_kind.descr(lev_candidate.def_id),
-                            ),
-                            lev_candidate.name,
-                            Applicability::MaybeIncorrect,
-                        );
+                        // Methods are defined within the context of a struct and their first parameter is always self,
+                        // which represents the instance of the struct the method is being called on
+                        // Associated functions don’t take self as a parameter and
+                        // they are not methods because they don’t have an instance of the struct to work with.
+                        if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter {
+                            err.span_suggestion(
+                                span,
+                                &format!("there is a method with a similar name",),
+                                lev_candidate.name,
+                                Applicability::MaybeIncorrect,
+                            );
+                        } else {
+                            err.span_suggestion(
+                                span,
+                                &format!(
+                                    "there is {} {} with a similar name",
+                                    def_kind.article(),
+                                    def_kind.descr(lev_candidate.def_id),
+                                ),
+                                lev_candidate.name,
+                                Applicability::MaybeIncorrect,
+                            );
+                        }
                     }
                 }