about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_ast/src/lib.rs1
-rw-r--r--compiler/rustc_ast/src/util/literal.rs39
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs4
-rw-r--r--compiler/rustc_attr/src/builtin.rs41
-rw-r--r--compiler/rustc_borrowck/src/borrowck_errors.rs64
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs36
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs30
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mod.rs20
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/move_errors.rs17
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs8
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs6
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_errors.rs16
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs4
-rw-r--r--compiler/rustc_borrowck/src/lib.rs28
-rw-r--r--compiler/rustc_borrowck/src/nll.rs2
-rw-r--r--compiler/rustc_borrowck/src/region_infer/mod.rs3
-rw-r--r--compiler/rustc_borrowck/src/universal_regions.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/asm.rs15
-rw-r--r--compiler/rustc_builtin_macros/src/assert.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/cfg.rs8
-rw-r--r--compiler/rustc_builtin_macros/src/format.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs8
-rw-r--r--compiler/rustc_codegen_cranelift/src/main_shim.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/utils.rs33
-rw-r--r--compiler/rustc_codegen_ssa/src/back/write.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/base.rs5
-rw-r--r--compiler/rustc_const_eval/src/const_eval/error.rs40
-rw-r--r--compiler/rustc_const_eval/src/const_eval/eval_queries.rs3
-rw-r--r--compiler/rustc_const_eval/src/transform/check_consts/ops.rs184
-rw-r--r--compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs8
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs75
-rw-r--r--compiler/rustc_errors/src/diagnostic_builder.rs354
-rw-r--r--compiler/rustc_errors/src/json.rs10
-rw-r--r--compiler/rustc_errors/src/lib.rs178
-rw-r--r--compiler/rustc_expand/src/base.rs20
-rw-r--r--compiler/rustc_expand/src/mbe/macro_rules.rs12
-rw-r--r--compiler/rustc_expand/src/module.rs6
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs4
-rw-r--r--compiler/rustc_hir/src/hir.rs3
-rw-r--r--compiler/rustc_hir/src/intravisit.rs2
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs52
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs14
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs6
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs12
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs8
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs12
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs16
-rw-r--r--compiler/rustc_infer/src/traits/error_reporting/mod.rs6
-rw-r--r--compiler/rustc_interface/src/interface.rs26
-rw-r--r--compiler/rustc_lexer/src/unescape.rs45
-rw-r--r--compiler/rustc_lint/src/builtin.rs23
-rw-r--r--compiler/rustc_lint/src/context.rs57
-rw-r--r--compiler/rustc_lint/src/levels.rs25
-rw-r--r--compiler/rustc_lint/src/lib.rs1
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs1
-rw-r--r--compiler/rustc_macros/src/session_diagnostic.rs2
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs38
-rw-r--r--compiler/rustc_metadata/src/rmeta/encoder.rs4
-rw-r--r--compiler/rustc_metadata/src/rmeta/mod.rs4
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs3
-rw-r--r--compiler/rustc_middle/src/lint.rs20
-rw-r--r--compiler/rustc_middle/src/metadata.rs2
-rw-r--r--compiler/rustc_middle/src/middle/stability.rs4
-rw-r--r--compiler/rustc_middle/src/mir/interpret/error.rs5
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/diagnostics.rs8
-rw-r--r--compiler/rustc_middle/src/ty/error.rs26
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs19
-rw-r--r--compiler/rustc_mir_transform/src/check_const_item_mutation.rs2
-rw-r--r--compiler/rustc_monomorphize/src/collector.rs5
-rw-r--r--compiler/rustc_parse/src/lexer/mod.rs6
-rw-r--r--compiler/rustc_parse/src/lexer/unescape_error_reporting.rs26
-rw-r--r--compiler/rustc_parse/src/lexer/unicode_chars.rs4
-rw-r--r--compiler/rustc_parse/src/parser/attr.rs12
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs81
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs40
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs2
-rw-r--r--compiler/rustc_parse/src/parser/item.rs16
-rw-r--r--compiler/rustc_parse/src/parser/mod.rs8
-rw-r--r--compiler/rustc_parse/src/parser/pat.rs8
-rw-r--r--compiler/rustc_parse/src/parser/path.rs2
-rw-r--r--compiler/rustc_parse/src/parser/stmt.rs11
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs3
-rw-r--r--compiler/rustc_passes/src/check_attr.rs4
-rw-r--r--compiler/rustc_passes/src/check_const.rs4
-rw-r--r--compiler/rustc_passes/src/intrinsicck.rs2
-rw-r--r--compiler/rustc_privacy/src/lib.rs4
-rw-r--r--compiler/rustc_query_system/src/query/config.rs4
-rw-r--r--compiler/rustc_query_system/src/query/job.rs4
-rw-r--r--compiler/rustc_query_system/src/query/plumbing.rs4
-rw-r--r--compiler/rustc_resolve/src/access_levels.rs5
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs11
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs15
-rw-r--r--compiler/rustc_resolve/src/imports.rs22
-rw-r--r--compiler/rustc_resolve/src/late.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs43
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs5
-rw-r--r--compiler/rustc_resolve/src/lib.rs20
-rw-r--r--compiler/rustc_resolve/src/macros.rs8
-rw-r--r--compiler/rustc_save_analysis/src/sig.rs2
-rw-r--r--compiler/rustc_session/src/config.rs48
-rw-r--r--compiler/rustc_session/src/parse.rs8
-rw-r--r--compiler/rustc_session/src/session.rs90
-rw-r--r--compiler/rustc_trait_selection/src/traits/coherence.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs61
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs126
-rw-r--r--compiler/rustc_trait_selection/src/traits/misc.rs15
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs9
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs4
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/check/_match.rs4
-rw-r--r--compiler/rustc_typeck/src/check/callee.rs6
-rw-r--r--compiler/rustc_typeck/src/check/cast.rs2
-rw-r--r--compiler/rustc_typeck/src/check/check.rs20
-rw-r--r--compiler/rustc_typeck/src/check/coercion.rs10
-rw-r--r--compiler/rustc_typeck/src/check/demand.rs32
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs50
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs6
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/checks.rs8
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs30
-rw-r--r--compiler/rustc_typeck/src/check/method/mod.rs4
-rw-r--r--compiler/rustc_typeck/src/check/method/suggest.rs36
-rw-r--r--compiler/rustc_typeck/src/check/mod.rs8
-rw-r--r--compiler/rustc_typeck/src/check/op.rs8
-rw-r--r--compiler/rustc_typeck/src/check/pat.rs24
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs20
-rw-r--r--compiler/rustc_typeck/src/coherence/builtin.rs5
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs7
-rw-r--r--compiler/rustc_typeck/src/collect.rs32
-rw-r--r--compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs11
-rw-r--r--compiler/rustc_typeck/src/structured_errors.rs16
-rw-r--r--compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs27
-rw-r--r--compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs31
-rw-r--r--compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs26
-rw-r--r--library/core/benches/slice.rs43
-rw-r--r--library/core/src/mem/mod.rs45
-rw-r--r--library/core/src/ptr/mod.rs132
-rw-r--r--src/doc/unstable-book/src/compiler-flags/check-cfg.md221
-rw-r--r--src/librustdoc/clean/mod.rs2
-rw-r--r--src/librustdoc/clean/render_macro_matchers.rs6
-rw-r--r--src/librustdoc/doctest.rs9
-rw-r--r--src/librustdoc/lib.rs8
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs19
-rw-r--r--src/librustdoc/visit_ast.rs2
-rw-r--r--src/test/codegen/swap-large-types.rs64
-rw-r--r--src/test/codegen/swap-simd-types.rs32
-rw-r--r--src/test/codegen/swap-small-types.rs44
-rw-r--r--src/test/ui-fulldeps/pprust-expr-roundtrip.rs2
-rw-r--r--src/test/ui/auto-traits/suspicious-impls-lint.rs10
-rw-r--r--src/test/ui/auto-traits/suspicious-impls-lint.stderr29
-rw-r--r--src/test/ui/check-cfg/invalid-cfg-name.stderr2
-rw-r--r--src/test/ui/check-cfg/invalid-cfg-value.stderr1
-rw-r--r--src/test/ui/check-cfg/mix.rs50
-rw-r--r--src/test/ui/check-cfg/mix.stderr66
-rw-r--r--src/test/ui/check-cfg/no-values.rs10
-rw-r--r--src/test/ui/check-cfg/no-values.stderr11
-rw-r--r--src/test/ui/check-cfg/well-known-names.rs27
-rw-r--r--src/test/ui/check-cfg/well-known-names.stderr26
-rw-r--r--src/test/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs (renamed from src/test/ui/debuginfo-emit-llvm-ir-and-split-debuginfo.rs)0
-rw-r--r--src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs29
-rw-r--r--src/test/ui/traits/copy-impl-cannot-normalize.rs25
-rw-r--r--src/test/ui/traits/copy-impl-cannot-normalize.stderr14
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/inspector.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs6
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.fixed2
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr2
-rw-r--r--src/tools/rustfmt/src/modules.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/cfg_if.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/lazy_static.rs2
-rw-r--r--src/tools/rustfmt/src/parse/macros/mod.rs2
-rw-r--r--src/tools/rustfmt/src/parse/parser.rs2
-rw-r--r--src/tools/rustfmt/src/parse/session.rs29
189 files changed, 2575 insertions, 1411 deletions
diff --git a/compiler/rustc_ast/src/lib.rs b/compiler/rustc_ast/src/lib.rs
index 84fe9ad2672..21183121e15 100644
--- a/compiler/rustc_ast/src/lib.rs
+++ b/compiler/rustc_ast/src/lib.rs
@@ -16,6 +16,7 @@
 #![feature(min_specialization)]
 #![recursion_limit = "256"]
 #![feature(slice_internals)]
+#![feature(stmt_expr_attributes)]
 
 #[macro_use]
 extern crate rustc_macros;
diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs
index 1cc5ddfd8ee..224afbd553f 100644
--- a/compiler/rustc_ast/src/util/literal.rs
+++ b/compiler/rustc_ast/src/util/literal.rs
@@ -56,25 +56,30 @@ impl LitKind {
                 // new symbol because the string in the LitKind is different to the
                 // string in the token.
                 let s = symbol.as_str();
-                let symbol =
-                    if s.contains(&['\\', '\r']) {
-                        let mut buf = String::with_capacity(s.len());
-                        let mut error = Ok(());
-                        unescape_literal(&s, Mode::Str, &mut |_, unescaped_char| {
-                            match unescaped_char {
-                                Ok(c) => buf.push(c),
-                                Err(err) => {
-                                    if err.is_fatal() {
-                                        error = Err(LitError::LexerError);
-                                    }
+                let symbol = if s.contains(&['\\', '\r']) {
+                    let mut buf = String::with_capacity(s.len());
+                    let mut error = Ok(());
+                    // Force-inlining here is aggressive but the closure is
+                    // called on every char in the string, so it can be
+                    // hot in programs with many long strings.
+                    unescape_literal(
+                        &s,
+                        Mode::Str,
+                        &mut #[inline(always)]
+                        |_, unescaped_char| match unescaped_char {
+                            Ok(c) => buf.push(c),
+                            Err(err) => {
+                                if err.is_fatal() {
+                                    error = Err(LitError::LexerError);
                                 }
                             }
-                        });
-                        error?;
-                        Symbol::intern(&buf)
-                    } else {
-                        symbol
-                    };
+                        },
+                    );
+                    error?;
+                    Symbol::intern(&buf)
+                } else {
+                    symbol
+                };
                 LitKind::Str(symbol, ast::StrStyle::Cooked)
             }
             token::StrRaw(n) => {
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index a8633336512..3ddc7fce1b7 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -444,8 +444,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
             ),
             ItemKind::MacroDef(MacroDef { ref body, macro_rules }) => {
                 let body = P(self.lower_mac_args(body));
-
-                hir::ItemKind::Macro(ast::MacroDef { body, macro_rules })
+                let macro_kind = self.resolver.decl_macro_kind(self.resolver.local_def_id(id));
+                hir::ItemKind::Macro(ast::MacroDef { body, macro_rules }, macro_kind)
             }
             ItemKind::MacCall(..) => {
                 panic!("`TyMac` should have been expanded by now")
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 0a59e3c2e3f..0156c5016ac 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -61,7 +61,7 @@ use rustc_session::lint::LintBuffer;
 use rustc_session::parse::feature_err;
 use rustc_session::utils::{FlattenNonterminals, NtToTokenstream};
 use rustc_session::Session;
-use rustc_span::hygiene::ExpnId;
+use rustc_span::hygiene::{ExpnId, MacroKind};
 use rustc_span::source_map::{respan, DesugaringKind};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -210,6 +210,8 @@ pub trait ResolverAstLowering {
         expn_id: ExpnId,
         span: Span,
     ) -> LocalDefId;
+
+    fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind;
 }
 
 /// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree,
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index 8d6c8c24785..68b536da9f7 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -8,6 +8,7 @@ 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;
+use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_session::parse::{feature_err, ParseSess};
 use rustc_session::Session;
 use rustc_span::hygiene::Transparency;
@@ -461,29 +462,37 @@ pub fn cfg_matches(cfg: &ast::MetaItem, sess: &ParseSess, features: Option<&Feat
                 true
             }
             MetaItemKind::NameValue(..) | MetaItemKind::Word => {
-                let name = cfg.ident().expect("multi-segment cfg predicate").name;
+                let ident = cfg.ident().expect("multi-segment cfg predicate");
+                let name = ident.name;
                 let value = cfg.value_str();
-                if sess.check_config.names_checked && !sess.check_config.names_valid.contains(&name)
-                {
-                    sess.buffer_lint(
-                        UNEXPECTED_CFGS,
-                        cfg.span,
-                        CRATE_NODE_ID,
-                        "unexpected `cfg` condition name",
-                    );
-                }
-                if let Some(val) = value {
-                    if sess.check_config.values_checked.contains(&name)
-                        && !sess.check_config.values_valid.contains(&(name, val))
-                    {
-                        sess.buffer_lint(
+                if let Some(names_valid) = &sess.check_config.names_valid {
+                    if !names_valid.contains(&name) {
+                        sess.buffer_lint_with_diagnostic(
                             UNEXPECTED_CFGS,
                             cfg.span,
                             CRATE_NODE_ID,
-                            "unexpected `cfg` condition value",
+                            "unexpected `cfg` condition name",
+                            BuiltinLintDiagnostics::UnexpectedCfg(ident.span, name, None),
                         );
                     }
                 }
+                if let Some(value) = value {
+                    if let Some(values) = &sess.check_config.values_valid.get(&name) {
+                        if !values.contains(&value) {
+                            sess.buffer_lint_with_diagnostic(
+                                UNEXPECTED_CFGS,
+                                cfg.span,
+                                CRATE_NODE_ID,
+                                "unexpected `cfg` condition value",
+                                BuiltinLintDiagnostics::UnexpectedCfg(
+                                    cfg.name_value_literal_span().unwrap(),
+                                    name,
+                                    Some(value),
+                                ),
+                            );
+                        }
+                    }
+                }
                 sess.config.contains(&(name, value))
             }
         }
diff --git a/compiler/rustc_borrowck/src/borrowck_errors.rs b/compiler/rustc_borrowck/src/borrowck_errors.rs
index 7140cda8e4e..e2b6a48a9e8 100644
--- a/compiler/rustc_borrowck/src/borrowck_errors.rs
+++ b/compiler/rustc_borrowck/src/borrowck_errors.rs
@@ -1,9 +1,13 @@
-use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{struct_span_err, DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_span::{MultiSpan, Span};
 
 impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
-    crate fn cannot_move_when_borrowed(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> {
+    crate fn cannot_move_when_borrowed(
+        &self,
+        span: Span,
+        desc: &str,
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0505, "cannot move out of {} because it is borrowed", desc,)
     }
 
@@ -13,7 +17,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         desc: &str,
         borrow_span: Span,
         borrow_desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -32,7 +36,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         verb: &str,
         desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(
             self,
             span,
@@ -51,7 +55,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_loan_span: Span,
         old_opt_via: &str,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let via =
             |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
         let mut err = struct_span_err!(
@@ -99,7 +103,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         desc: &str,
         old_loan_span: Span,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -132,7 +136,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         noun_old: &str,
         old_opt_via: &str,
         previous_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -164,7 +168,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         old_opt_via: &str,
         previous_end_span: Option<Span>,
         second_borrow_desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             new_loan_span,
@@ -200,7 +204,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         kind_old: &str,
         msg_old: &str,
         old_load_end_span: Option<Span>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let via =
             |msg: &str| if msg.is_empty() { "".to_string() } else { format!(" (via {})", msg) };
         let mut err = struct_span_err!(
@@ -243,7 +247,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         borrow_span: Span,
         desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -262,12 +266,12 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         desc: &str,
         is_arg: bool,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let msg = if is_arg { "to immutable argument" } else { "twice to immutable variable" };
         struct_span_err!(self, span, E0384, "cannot assign {} {}", msg, desc)
     }
 
-    crate fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx> {
+    crate fn cannot_assign(&self, span: Span, desc: &str) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0594, "cannot assign to {}", desc)
     }
 
@@ -275,7 +279,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         move_from_span: Span,
         move_from_desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, move_from_span, E0507, "cannot move out of {}", move_from_desc,)
     }
 
@@ -287,7 +291,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         move_from_span: Span,
         ty: Ty<'_>,
         is_index: Option<bool>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let type_name = match (&ty.kind(), is_index) {
             (&ty::Array(_, _), Some(true)) | (&ty::Array(_, _), None) => "array",
             (&ty::Slice(_), _) => "slice",
@@ -309,7 +313,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         move_from_span: Span,
         container_ty: Ty<'_>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             move_from_span,
@@ -327,7 +331,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         verb: &str,
         optional_adverb_for_moved: &str,
         moved_path: Option<String>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let moved_path = moved_path.map(|mp| format!(": `{}`", mp)).unwrap_or_default();
 
         struct_span_err!(
@@ -346,7 +350,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         span: Span,
         path: &str,
         reason: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0596, "cannot borrow {} as mutable{}", path, reason,)
     }
 
@@ -357,7 +361,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         immutable_place: &str,
         immutable_section: &str,
         action: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             mutate_span,
@@ -376,7 +380,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         span: Span,
         yield_span: Span,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -387,7 +391,10 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         err
     }
 
-    crate fn cannot_borrow_across_destructor(&self, borrow_span: Span) -> DiagnosticBuilder<'cx> {
+    crate fn cannot_borrow_across_destructor(
+        &self,
+        borrow_span: Span,
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(
             self,
             borrow_span,
@@ -400,7 +407,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         span: Span,
         path: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0597, "{} does not live long enough", path,)
     }
 
@@ -410,7 +417,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         return_kind: &str,
         reference_desc: &str,
         path_desc: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             span,
@@ -435,7 +442,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         closure_kind: &str,
         borrowed_path: &str,
         capture_span: Span,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let mut err = struct_span_err!(
             self,
             closure_span,
@@ -454,11 +461,14 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
     crate fn thread_local_value_does_not_live_long_enough(
         &self,
         span: Span,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0712, "thread-local variable borrowed past end of function",)
     }
 
-    crate fn temporary_value_borrowed_for_too_long(&self, span: Span) -> DiagnosticBuilder<'cx> {
+    crate fn temporary_value_borrowed_for_too_long(
+        &self,
+        span: Span,
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         struct_span_err!(self, span, E0716, "temporary value dropped while borrowed",)
     }
 
@@ -467,7 +477,7 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
         sp: S,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         self.infcx.tcx.sess.struct_span_err_with_code(sp, msg, code)
     }
 }
@@ -476,7 +486,7 @@ crate fn borrowed_data_escapes_closure<'tcx>(
     tcx: TyCtxt<'tcx>,
     escape_span: Span,
     escapes_from: &str,
-) -> DiagnosticBuilder<'tcx> {
+) -> DiagnosticBuilder<'tcx, ErrorReported> {
     struct_span_err!(
         tcx.sess,
         escape_span,
diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
index 2a906e41b8c..3ef002bf740 100644
--- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs
@@ -1,4 +1,4 @@
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::{DiagnosticBuilder, ErrorReported};
 use rustc_infer::infer::canonical::Canonical;
 use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
 use rustc_infer::infer::region_constraints::Constraint;
@@ -120,7 +120,11 @@ impl<'tcx, F, G> ToUniverseInfo<'tcx> for Canonical<'tcx, type_op::custom::Custo
 trait TypeOpInfo<'tcx> {
     /// Returns an error to be reported if rerunning the type op fails to
     /// recover the error's cause.
-    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
+    fn fallback_error(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported>;
 
     fn base_universe(&self) -> ty::UniverseIndex;
 
@@ -130,7 +134,7 @@ trait TypeOpInfo<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx>>;
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>>;
 
     fn report_error(
         &self,
@@ -188,7 +192,11 @@ struct PredicateQuery<'tcx> {
 }
 
 impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
-    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn fallback_error(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
         err.note(&format!("could not prove {}", self.canonical_query.value.value.predicate));
         err
@@ -204,7 +212,7 @@ impl<'tcx> TypeOpInfo<'tcx> for PredicateQuery<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         tcx.infer_ctxt().enter_with_canonical(
             cause.span,
             &self.canonical_query,
@@ -231,7 +239,11 @@ impl<'tcx, T> TypeOpInfo<'tcx> for NormalizeQuery<'tcx, T>
 where
     T: Copy + fmt::Display + TypeFoldable<'tcx> + 'tcx,
 {
-    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn fallback_error(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = tcx.sess.struct_span_err(span, "higher-ranked lifetime error");
         err.note(&format!("could not normalize `{}`", self.canonical_query.value.value.value));
         err
@@ -247,7 +259,7 @@ where
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         tcx.infer_ctxt().enter_with_canonical(
             cause.span,
             &self.canonical_query,
@@ -288,7 +300,11 @@ struct AscribeUserTypeQuery<'tcx> {
 }
 
 impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
-    fn fallback_error(&self, tcx: TyCtxt<'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn fallback_error(
+        &self,
+        tcx: TyCtxt<'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         // FIXME: This error message isn't great, but it doesn't show up in the existing UI tests,
         // and is only the fallback when the nice error fails. Consider improving this some more.
         tcx.sess.struct_span_err(span, "higher-ranked lifetime error")
@@ -304,7 +320,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> {
         cause: ObligationCause<'tcx>,
         placeholder_region: ty::Region<'tcx>,
         error_region: Option<ty::Region<'tcx>>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         tcx.infer_ctxt().enter_with_canonical(
             cause.span,
             &self.canonical_query,
@@ -329,7 +345,7 @@ fn try_extract_error_from_fulfill_cx<'tcx>(
     infcx: &InferCtxt<'_, 'tcx>,
     placeholder_region: ty::Region<'tcx>,
     error_region: Option<ty::Region<'tcx>>,
-) -> Option<DiagnosticBuilder<'tcx>> {
+) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
     let tcx = infcx.tcx;
 
     // We generally shouldn't have errors here because the query was
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index baa59efbf8d..cd1f73d5298 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -1,7 +1,7 @@
 use either::Either;
 use rustc_const_eval::util::{CallDesugaringKind, CallKind};
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
@@ -507,7 +507,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         location: Location,
         (place, _span): (Place<'tcx>, Span),
         borrow: &BorrowData<'tcx>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let borrow_spans = self.retrieve_borrow_spans(borrow);
         let borrow_span = borrow_spans.args_or_use();
 
@@ -554,7 +554,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         (place, span): (Place<'tcx>, Span),
         gen_borrow_kind: BorrowKind,
         issued_borrow: &BorrowData<'tcx>,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let issued_spans = self.retrieve_borrow_spans(issued_borrow);
         let issued_span = issued_spans.args_or_use();
 
@@ -782,7 +782,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     #[instrument(level = "debug", skip(self, err))]
     fn suggest_using_local_if_applicable(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         location: Location,
         (place, span): (Place<'tcx>, Span),
         gen_borrow_kind: BorrowKind,
@@ -855,7 +855,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
 
     fn suggest_split_at_mut_if_applicable(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         place: Place<'tcx>,
         borrowed_place: Place<'tcx>,
     ) {
@@ -1120,7 +1120,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         drop_span: Span,
         borrow_spans: UseSpans<'tcx>,
         explanation: BorrowExplanation,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         debug!(
             "report_local_value_does_not_live_long_enough(\
              {:?}, {:?}, {:?}, {:?}, {:?}\
@@ -1298,7 +1298,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &mut self,
         drop_span: Span,
         borrow_span: Span,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         debug!(
             "report_thread_local_value_does_not_live_long_enough(\
              {:?}, {:?}\
@@ -1325,7 +1325,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         borrow_spans: UseSpans<'tcx>,
         proper_span: Span,
         explanation: BorrowExplanation,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         debug!(
             "report_temporary_value_does_not_live_long_enough(\
              {:?}, {:?}, {:?}, {:?}\
@@ -1384,7 +1384,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         return_span: Span,
         category: ConstraintCategory,
         opt_place_desc: Option<&String>,
-    ) -> Option<DiagnosticBuilder<'cx>> {
+    ) -> Option<DiagnosticBuilder<'cx, ErrorReported>> {
         let return_kind = match category {
             ConstraintCategory::Return(_) => "return",
             ConstraintCategory::Yield => "yield",
@@ -1483,7 +1483,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         category: ConstraintCategory,
         constraint_span: Span,
         captured_var: &str,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let tcx = self.infcx.tcx;
         let args_span = use_span.args_or_use();
 
@@ -1560,7 +1560,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         upvar_span: Span,
         upvar_name: &str,
         escape_span: Span,
-    ) -> DiagnosticBuilder<'cx> {
+    ) -> DiagnosticBuilder<'cx, ErrorReported> {
         let tcx = self.infcx.tcx;
 
         let (_, escapes_from) = tcx.article_and_description(self.mir_def_id().to_def_id());
@@ -1835,7 +1835,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         self.buffer_error(err);
     }
 
-    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut DiagnosticBuilder<'_>) {
+    fn explain_deref_coercion(&mut self, loan: &BorrowData<'tcx>, err: &mut Diagnostic) {
         let tcx = self.infcx.tcx;
         if let (
             Some(Terminator { kind: TerminatorKind::Call { from_hir_call: false, .. }, .. }),
@@ -2362,11 +2362,7 @@ enum AnnotatedBorrowFnSignature<'tcx> {
 impl<'tcx> AnnotatedBorrowFnSignature<'tcx> {
     /// Annotate the provided diagnostic with information about borrow from the fn signature that
     /// helps explain.
-    pub(crate) fn emit(
-        &self,
-        cx: &mut MirBorrowckCtxt<'_, 'tcx>,
-        diag: &mut DiagnosticBuilder<'_>,
-    ) -> String {
+    pub(crate) fn emit(&self, cx: &mut MirBorrowckCtxt<'_, 'tcx>, diag: &mut Diagnostic) -> String {
         match self {
             &AnnotatedBorrowFnSignature::Closure { argument_ty, argument_span } => {
                 diag.span_label(
diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
index b1e5a211cf1..da6610c002e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
@@ -3,7 +3,7 @@
 use std::collections::VecDeque;
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_index::vec::IndexVec;
 use rustc_infer::infer::NllRegionVariableOrigin;
 use rustc_middle::mir::{
@@ -60,7 +60,7 @@ impl BorrowExplanation {
         tcx: TyCtxt<'tcx>,
         body: &Body<'tcx>,
         local_names: &IndexVec<Local, Option<Symbol>>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         borrow_desc: &str,
         borrow_span: Option<Span>,
         multiple_borrow_span: Option<(Span, Span)>,
@@ -275,7 +275,7 @@ impl BorrowExplanation {
     }
     pub(crate) fn add_lifetime_bound_suggestion_to_diagnostic(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         category: &ConstraintCategory,
         span: Span,
         region_name: &RegionName,
diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs
index 7f94e357f37..754856043b3 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mod.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs
@@ -1,7 +1,7 @@
 //! Borrow checker diagnostics.
 
 use rustc_const_eval::util::call_kind;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
@@ -57,7 +57,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
         &self,
         location: Location,
         place: PlaceRef<'tcx>,
-        diag: &mut DiagnosticBuilder<'_>,
+        diag: &mut Diagnostic,
     ) {
         debug!("add_moved_or_invoked_closure_note: location={:?} place={:?}", location, place);
         let mut target = place.local_or_deref_local();
@@ -409,7 +409,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
     /// Add a note that a type does not implement `Copy`
     pub(super) fn note_type_does_not_implement_copy(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         place_desc: &str,
         ty: Ty<'tcx>,
         span: Option<Span>,
@@ -609,11 +609,7 @@ impl UseSpans<'_> {
     }
 
     // Add a span label to the arguments of the closure, if it exists.
-    pub(super) fn args_span_label(
-        self,
-        err: &mut DiagnosticBuilder<'_>,
-        message: impl Into<String>,
-    ) {
+    pub(super) fn args_span_label(self, err: &mut Diagnostic, message: impl Into<String>) {
         if let UseSpans::ClosureUse { args_span, .. } = self {
             err.span_label(args_span, message);
         }
@@ -621,11 +617,7 @@ impl UseSpans<'_> {
 
     // Add a span label to the use of the captured variable, if it exists.
     // only adds label to the `path_span`
-    pub(super) fn var_span_label_path_only(
-        self,
-        err: &mut DiagnosticBuilder<'_>,
-        message: impl Into<String>,
-    ) {
+    pub(super) fn var_span_label_path_only(self, err: &mut Diagnostic, message: impl Into<String>) {
         if let UseSpans::ClosureUse { path_span, .. } = self {
             err.span_label(path_span, message);
         }
@@ -634,7 +626,7 @@ impl UseSpans<'_> {
     // Add a span label to the use of the captured variable, if it exists.
     pub(super) fn var_span_label(
         self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         message: impl Into<String>,
         kind_desc: impl Into<String>,
     ) {
diff --git a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
index d4f238ff71f..66f4c28a36d 100644
--- a/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/move_errors.rs
@@ -1,5 +1,5 @@
 use rustc_const_eval::util::CallDesugaringKind;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_middle::mir::*;
 use rustc_middle::ty;
@@ -271,7 +271,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         &mut self,
         place: Place<'tcx>,
         span: Span,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let description = if place.projection.len() == 1 {
             format!("static item {}", self.describe_any_place(place.as_ref()))
         } else {
@@ -293,7 +293,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         deref_target_place: Place<'tcx>,
         span: Span,
         use_spans: Option<UseSpans<'tcx>>,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         // Inspect the type of the content behind the
         // borrow to provide feedback about why this
         // was a move rather than a copy.
@@ -441,12 +441,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         err
     }
 
-    fn add_move_hints(
-        &self,
-        error: GroupedMoveError<'tcx>,
-        err: &mut DiagnosticBuilder<'a>,
-        span: Span,
-    ) {
+    fn add_move_hints(&self, error: GroupedMoveError<'tcx>, err: &mut Diagnostic, span: Span) {
         match error {
             GroupedMoveError::MovesFromPlace { mut binds_to, move_from, .. } => {
                 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
@@ -505,7 +500,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn add_move_error_suggestions(&self, err: &mut DiagnosticBuilder<'a>, binds_to: &[Local]) {
+    fn add_move_error_suggestions(&self, err: &mut Diagnostic, binds_to: &[Local]) {
         let mut suggestions: Vec<(Span, &str, String)> = Vec::new();
         for local in binds_to {
             let bind_to = &self.body.local_decls[*local];
@@ -541,7 +536,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         }
     }
 
-    fn add_move_error_details(&self, err: &mut DiagnosticBuilder<'a>, binds_to: &[Local]) {
+    fn add_move_error_details(&self, err: &mut Diagnostic, binds_to: &[Local]) {
         for (j, local) in binds_to.iter().enumerate() {
             let bind_to = &self.body.local_decls[*local];
             let binding_span = bind_to.source_info.span;
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 7d0dde53c2b..2c9bd8ea96e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -17,7 +17,7 @@ use rustc_span::{BytePos, Span};
 use crate::diagnostics::BorrowedContentSource;
 use crate::MirBorrowckCtxt;
 use rustc_const_eval::util::collect_writes::FindAssignments;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 
 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 pub(crate) enum AccessKind {
@@ -689,7 +689,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         tcx: TyCtxt<'_>,
         id: &hir::def_id::DefId,
         the_place_err: PlaceRef<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     ) {
         let closure_local_def_id = id.expect_local();
         let tables = tcx.typeck(closure_local_def_id);
@@ -754,7 +754,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
     // Attempt to search similar mutable associated items for suggestion.
     // In the future, attempt in all path but initially for RHS of for_loop
-    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
         use hir::{
             BodyId, Expr,
             ExprKind::{Block, Call, DropTemps, Match, MethodCall},
@@ -843,7 +843,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     }
 
     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
-    fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
+    fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
         err.span_label(sp, format!("cannot {}", act));
 
         let hir = self.infcx.tcx.hir();
diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
index 21f00af5c0c..de50f907eff 100644
--- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs
@@ -2,7 +2,7 @@
 //! outlives constraints.
 
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_middle::ty::RegionVid;
 use smallvec::SmallVec;
 use std::collections::BTreeMap;
@@ -162,7 +162,7 @@ impl OutlivesSuggestionBuilder {
         &mut self,
         mbcx: &MirBorrowckCtxt<'_, '_>,
         errci: &ErrorConstraintInfo,
-        diag: &mut DiagnosticBuilder<'_>,
+        diag: &mut Diagnostic,
     ) {
         // Emit an intermediate note.
         let fr_name = self.region_vid_to_name(mbcx, errci.fr);
@@ -256,6 +256,6 @@ impl OutlivesSuggestionBuilder {
         diag.sort_span = mir_span.shrink_to_hi();
 
         // Buffer the diagnostic
-        mbcx.buffer_error(diag);
+        mbcx.buffer_non_error_diag(diag);
     }
 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
index e6a323d676e..64f05f6004f 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs
@@ -1,6 +1,6 @@
 //! Error reporting machinery for lifetime errors.
 
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_infer::infer::{
     error_reporting::nice_region_error::NiceRegionError,
     error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
@@ -392,7 +392,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         &self,
         errci: &ErrorConstraintInfo,
         kind: ReturnConstraint,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let ErrorConstraintInfo { outlived_fr, span, .. } = errci;
 
         let mut diag = self
@@ -469,7 +469,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// LL |     ref_obj(x)
     ///    |     ^^^^^^^^^^ `x` escapes the function body here
     /// ```
-    fn report_escaping_data_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
+    fn report_escaping_data_error(
+        &self,
+        errci: &ErrorConstraintInfo,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let ErrorConstraintInfo { span, category, .. } = errci;
 
         let fr_name_and_span = self.regioncx.get_var_name_and_span_for_region(
@@ -570,7 +573,10 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     ///    |     ^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it
     ///    |                    is returning data with lifetime `'b`
     /// ```
-    fn report_general_error(&self, errci: &ErrorConstraintInfo) -> DiagnosticBuilder<'tcx> {
+    fn report_general_error(
+        &self,
+        errci: &ErrorConstraintInfo,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let ErrorConstraintInfo {
             fr,
             fr_is_local,
@@ -632,7 +638,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
     /// ```
     fn add_static_impl_trait_suggestion(
         &self,
-        diag: &mut DiagnosticBuilder<'tcx>,
+        diag: &mut Diagnostic,
         fr: RegionVid,
         // We need to pass `fr_name` - computing it again will label it twice.
         fr_name: RegionName,
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 5d1d291d3b4..c9395492c9e 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -1,7 +1,7 @@
 use std::fmt::{self, Display};
 use std::iter;
 
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_middle::ty::print::RegionHighlightMode;
@@ -98,7 +98,7 @@ impl RegionName {
         }
     }
 
-    crate fn highlight_region_name(&self, diag: &mut DiagnosticBuilder<'_>) {
+    crate fn highlight_region_name(&self, diag: &mut Diagnostic) {
         match &self.source {
             RegionNameSource::NamedFreeRegion(span)
             | RegionNameSource::NamedEarlyBoundRegion(span) => {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 753eed53068..590c5799ff6 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -379,7 +379,7 @@ fn do_mir_borrowck<'a, 'tcx>(
     // Convert any reservation warnings into lints.
     let reservation_warnings = mem::take(&mut mbcx.reservation_warnings);
     for (_, (place, span, location, bk, borrow)) in reservation_warnings {
-        let mut initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
+        let initial_diag = mbcx.report_conflicting_borrow(location, (place, span), bk, &borrow);
 
         let scope = mbcx.body.source_info(location).scope;
         let lint_root = match &mbcx.body.source_scopes[scope].local_data {
@@ -398,7 +398,7 @@ fn do_mir_borrowck<'a, 'tcx>(
                 diag.message = initial_diag.styled_message().clone();
                 diag.span = initial_diag.span.clone();
 
-                mbcx.buffer_error(diag);
+                mbcx.buffer_non_error_diag(diag);
             },
         );
         initial_diag.cancel();
@@ -2293,8 +2293,8 @@ mod error {
         /// when errors in the map are being re-added to the error buffer so that errors with the
         /// same primary span come out in a consistent order.
         buffered_move_errors:
-            BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>)>,
-        /// Errors to be reported buffer
+            BTreeMap<Vec<MoveOutIndex>, (PlaceRef<'tcx>, DiagnosticBuilder<'tcx, ErrorReported>)>,
+        /// Diagnostics to be reported buffer.
         buffered: Vec<Diagnostic>,
         /// Set to Some if we emit an error during borrowck
         tainted_by_errors: Option<ErrorReported>,
@@ -2309,27 +2309,37 @@ mod error {
             }
         }
 
-        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
+        // FIXME(eddyb) this is a suboptimal API because `tainted_by_errors` is
+        // set before any emission actually happens (weakening the guarantee).
+        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorReported>) {
             self.tainted_by_errors = Some(ErrorReported {});
             t.buffer(&mut self.buffered);
         }
 
+        pub fn buffer_non_error_diag(&mut self, t: DiagnosticBuilder<'_, ()>) {
+            t.buffer(&mut self.buffered);
+        }
+
         pub fn set_tainted_by_errors(&mut self) {
             self.tainted_by_errors = Some(ErrorReported {});
         }
     }
 
     impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
-        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_>) {
+        pub fn buffer_error(&mut self, t: DiagnosticBuilder<'_, ErrorReported>) {
             self.errors.buffer_error(t);
         }
 
+        pub fn buffer_non_error_diag(&mut self, t: DiagnosticBuilder<'_, ()>) {
+            self.errors.buffer_non_error_diag(t);
+        }
+
         pub fn buffer_move_error(
             &mut self,
             move_out_indices: Vec<MoveOutIndex>,
-            place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx>),
+            place_and_err: (PlaceRef<'tcx>, DiagnosticBuilder<'tcx, ErrorReported>),
         ) -> bool {
-            if let Some((_, mut diag)) =
+            if let Some((_, diag)) =
                 self.errors.buffered_move_errors.insert(move_out_indices, place_and_err)
             {
                 // Cancel the old diagnostic so we don't ICE
@@ -2365,7 +2375,7 @@ mod error {
         pub fn has_move_error(
             &self,
             move_out_indices: &[MoveOutIndex],
-        ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx>)> {
+        ) -> Option<&(PlaceRef<'tcx>, DiagnosticBuilder<'cx, ErrorReported>)> {
             self.errors.buffered_move_errors.get(move_out_indices)
         }
     }
diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs
index a16bdf28673..a2736fd1156 100644
--- a/compiler/rustc_borrowck/src/nll.rs
+++ b/compiler/rustc_borrowck/src/nll.rs
@@ -417,7 +417,7 @@ pub(super) fn dump_annotation<'a, 'tcx>(
         err.note(&format!("Inferred opaque type values:\n{:#?}", opaque_type_values));
     }
 
-    errors.buffer_error(err);
+    errors.buffer_non_error_diag(err);
 }
 
 fn for_each_region_constraint(
diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs
index b99fb00599e..3f0ce7dea00 100644
--- a/compiler/rustc_borrowck/src/region_infer/mod.rs
+++ b/compiler/rustc_borrowck/src/region_infer/mod.rs
@@ -5,6 +5,7 @@ use rustc_data_structures::binary_search_util;
 use rustc_data_structures::frozen::Frozen;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::graph::scc::Sccs;
+use rustc_errors::Diagnostic;
 use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
 use rustc_hir::CRATE_HIR_ID;
 use rustc_index::vec::IndexVec;
@@ -510,7 +511,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
     }
 
     /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`.
-    crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut rustc_errors::DiagnosticBuilder<'_>) {
+    crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
         self.universal_regions.annotate(tcx, err)
     }
 
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index d21a9d86e5b..478dbe31fba 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -14,7 +14,7 @@
 
 use either::Either;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
@@ -336,7 +336,7 @@ impl<'tcx> UniversalRegions<'tcx> {
     /// that this region imposes on others. The methods in this file
     /// handle the part about dumping the inference context internal
     /// state.
-    crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>) {
+    crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
         match self.defining_ty {
             DefiningTy::Closure(def_id, substs) => {
                 err.note(&format!(
diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs
index ac37c4973d8..57ef46475dd 100644
--- a/compiler/rustc_builtin_macros/src/asm.rs
+++ b/compiler/rustc_builtin_macros/src/asm.rs
@@ -3,7 +3,7 @@ use rustc_ast::ptr::P;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, PResult};
 use rustc_expand::base::{self, *};
 use rustc_parse::parser::Parser;
 use rustc_parse_format as parse;
@@ -30,7 +30,7 @@ fn parse_args<'a>(
     sp: Span,
     tts: TokenStream,
     is_global_asm: bool,
-) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
+) -> PResult<'a, AsmArgs> {
     let mut p = ecx.new_parser_from_tts(tts);
     let sess = &ecx.sess.parse_sess;
     parse_asm_args(&mut p, sess, sp, is_global_asm)
@@ -43,7 +43,7 @@ pub fn parse_asm_args<'a>(
     sess: &'a ParseSess,
     sp: Span,
     is_global_asm: bool,
-) -> Result<AsmArgs, DiagnosticBuilder<'a>> {
+) -> PResult<'a, AsmArgs> {
     let diag = &sess.span_diagnostic;
 
     if p.token == token::Eof {
@@ -390,7 +390,7 @@ fn parse_options<'a>(
     p: &mut Parser<'a>,
     args: &mut AsmArgs,
     is_global_asm: bool,
-) -> Result<(), DiagnosticBuilder<'a>> {
+) -> PResult<'a, ()> {
     let span_start = p.prev_token.span;
 
     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
@@ -431,10 +431,7 @@ fn parse_options<'a>(
     Ok(())
 }
 
-fn parse_clobber_abi<'a>(
-    p: &mut Parser<'a>,
-    args: &mut AsmArgs,
-) -> Result<(), DiagnosticBuilder<'a>> {
+fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
     let span_start = p.prev_token.span;
 
     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
@@ -501,7 +498,7 @@ fn parse_clobber_abi<'a>(
 fn parse_reg<'a>(
     p: &mut Parser<'a>,
     explicit_reg: &mut bool,
-) -> Result<ast::InlineAsmRegOrRegClass, DiagnosticBuilder<'a>> {
+) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
     p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
     let result = match p.token.uninterpolate().kind {
         token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
diff --git a/compiler/rustc_builtin_macros/src/assert.rs b/compiler/rustc_builtin_macros/src/assert.rs
index 9a45dec55f3..a984980dea9 100644
--- a/compiler/rustc_builtin_macros/src/assert.rs
+++ b/compiler/rustc_builtin_macros/src/assert.rs
@@ -4,7 +4,7 @@ use rustc_ast::token;
 use rustc_ast::tokenstream::{DelimSpan, TokenStream};
 use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, PResult};
 use rustc_expand::base::*;
 use rustc_parse::parser::Parser;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -83,11 +83,7 @@ struct Assert {
     custom_message: Option<TokenStream>,
 }
 
-fn parse_assert<'a>(
-    cx: &mut ExtCtxt<'a>,
-    sp: Span,
-    stream: TokenStream,
-) -> Result<Assert, DiagnosticBuilder<'a>> {
+fn parse_assert<'a>(cx: &mut ExtCtxt<'a>, sp: Span, stream: TokenStream) -> PResult<'a, Assert> {
     let mut parser = cx.new_parser_from_tts(stream);
 
     if parser.token == token::Eof {
diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs
index 4c00162b556..1e1cf917c60 100644
--- a/compiler/rustc_builtin_macros/src/cfg.rs
+++ b/compiler/rustc_builtin_macros/src/cfg.rs
@@ -6,7 +6,7 @@ use rustc_ast as ast;
 use rustc_ast::token;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_attr as attr;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::PResult;
 use rustc_expand::base::{self, *};
 use rustc_span::Span;
 
@@ -29,11 +29,7 @@ pub fn expand_cfg(
     }
 }
 
-fn parse_cfg<'a>(
-    cx: &mut ExtCtxt<'a>,
-    sp: Span,
-    tts: TokenStream,
-) -> Result<ast::MetaItem, DiagnosticBuilder<'a>> {
+fn parse_cfg<'a>(cx: &mut ExtCtxt<'a>, sp: Span, tts: TokenStream) -> PResult<'a, ast::MetaItem> {
     let mut p = cx.new_parser_from_tts(tts);
 
     if p.token == token::Eof {
diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs
index 6141d00f697..31213412d45 100644
--- a/compiler/rustc_builtin_macros/src/format.rs
+++ b/compiler/rustc_builtin_macros/src/format.rs
@@ -7,7 +7,7 @@ use rustc_ast::tokenstream::TokenStream;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast::{token, BlockCheckMode, UnsafeSource};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, Applicability, DiagnosticBuilder};
+use rustc_errors::{pluralize, Applicability, PResult};
 use rustc_expand::base::{self, *};
 use rustc_parse_format as parse;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -130,7 +130,7 @@ fn parse_args<'a>(
     ecx: &mut ExtCtxt<'a>,
     sp: Span,
     tts: TokenStream,
-) -> Result<(P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>), DiagnosticBuilder<'a>> {
+) -> PResult<'a, (P<ast::Expr>, Vec<P<ast::Expr>>, FxHashMap<Symbol, usize>)> {
     let mut args = Vec::<P<ast::Expr>>::new();
     let mut names = FxHashMap::<Symbol, usize>::default();
 
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 418729e7843..7ee0fb9b817 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -376,9 +376,13 @@ fn get_test_runner(
     match &*meta_list {
         [single] => match single.meta_item() {
             Some(meta_item) if meta_item.is_word() => return Some(meta_item.path.clone()),
-            _ => sd.struct_span_err(span, "`test_runner` argument must be a path").emit(),
+            _ => {
+                sd.struct_span_err(span, "`test_runner` argument must be a path").emit();
+            }
         },
-        _ => sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit(),
+        _ => {
+            sd.struct_span_err(span, "`#![test_runner(..)]` accepts exactly 1 argument").emit();
+        }
     }
     None
 }
diff --git a/compiler/rustc_codegen_cranelift/src/main_shim.rs b/compiler/rustc_codegen_cranelift/src/main_shim.rs
index 9ce727279c2..2f71a70a449 100644
--- a/compiler/rustc_codegen_cranelift/src/main_shim.rs
+++ b/compiler/rustc_codegen_cranelift/src/main_shim.rs
@@ -51,7 +51,10 @@ pub(crate) fn maybe_create_entry_wrapper(
         // late-bound regions, since late-bound
         // regions must appear in the argument
         // listing.
-        let main_ret_ty = tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap());
+        let main_ret_ty = tcx.normalize_erasing_regions(
+            ty::ParamEnv::reveal_all(),
+            main_ret_ty.no_bound_vars().unwrap(),
+        );
 
         let cmain_sig = Signature {
             params: vec![
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 4ca92b3efe0..d515502d445 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -449,7 +449,10 @@ fn pointer_or_reference_metadata<'ll, 'tcx>(
             // This is a thin pointer. Create a regular pointer type and give it the correct name.
             debug_assert_eq!(
                 (thin_pointer_size, thin_pointer_align),
-                cx.size_and_align_of(ptr_type)
+                cx.size_and_align_of(ptr_type),
+                "ptr_type={}, pointee_type={}",
+                ptr_type,
+                pointee_type,
             );
 
             unsafe {
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
index acd032a7dc6..fa75463067f 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/utils.rs
@@ -4,9 +4,9 @@ use super::namespace::item_namespace;
 use super::CrateDebugContext;
 
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::layout::{HasParamEnv, LayoutOf};
 use rustc_middle::ty::{self, DefIdTree, Ty};
-use rustc_target::abi::Variants;
+use tracing::trace;
 
 use crate::common::CodegenCx;
 use crate::llvm;
@@ -63,30 +63,26 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
     cx: &CodegenCx<'ll, 'tcx>,
     pointee_ty: Ty<'tcx>,
 ) -> Option<FatPtrKind> {
-    let layout = cx.layout_of(pointee_ty);
+    let pointee_tail_ty = cx.tcx.struct_tail_erasing_lifetimes(pointee_ty, cx.param_env());
+    let layout = cx.layout_of(pointee_tail_ty);
+    trace!(
+        "fat_pointer_kind: {:?} has layout {:?} (is_unsized? {})",
+        pointee_tail_ty,
+        layout,
+        layout.is_unsized()
+    );
 
     if !layout.is_unsized() {
         return None;
     }
 
-    match *pointee_ty.kind() {
+    match *pointee_tail_ty.kind() {
         ty::Str | ty::Slice(_) => Some(FatPtrKind::Slice),
         ty::Dynamic(..) => Some(FatPtrKind::Dyn),
-        ty::Adt(..) | ty::Tuple(..) if matches!(layout.variants, Variants::Single { .. }) => {
-            let last_field_index = layout.fields.count() - 1;
-            debug_assert!(
-                (0..last_field_index)
-                    .all(|field_index| { !layout.field(cx, field_index).is_unsized() })
-            );
-
-            let unsized_field = layout.field(cx, last_field_index);
-            debug_assert!(unsized_field.is_unsized());
-            fat_pointer_kind(cx, unsized_field.ty)
-        }
         ty::Foreign(_) => {
             // Assert that pointers to foreign types really are thin:
             debug_assert_eq!(
-                cx.size_of(cx.tcx.mk_imm_ptr(pointee_ty)),
+                cx.size_of(cx.tcx.mk_imm_ptr(pointee_tail_ty)),
                 cx.size_of(cx.tcx.mk_imm_ptr(cx.tcx.types.u8))
             );
             None
@@ -94,7 +90,10 @@ pub(crate) fn fat_pointer_kind<'ll, 'tcx>(
         _ => {
             // For all other pointee types we should already have returned None
             // at the beginning of the function.
-            panic!("fat_pointer_kind() - Encountered unexpected `pointee_ty`: {:?}", pointee_ty)
+            panic!(
+                "fat_pointer_kind() - Encountered unexpected `pointee_tail_ty`: {:?}",
+                pointee_tail_ty
+            )
         }
     }
 }
diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs
index f6fddbb509d..55ea0c4d727 100644
--- a/compiler/rustc_codegen_ssa/src/back/write.rs
+++ b/compiler/rustc_codegen_ssa/src/back/write.rs
@@ -1709,7 +1709,7 @@ impl Emitter for SharedEmitter {
         drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
             msg: diag.message(),
             code: diag.code.clone(),
-            lvl: diag.level,
+            lvl: diag.level(),
         })));
         for child in &diag.children {
             drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic {
@@ -1753,7 +1753,7 @@ impl SharedEmitterMain {
                     let msg = msg.strip_prefix("error: ").unwrap_or(&msg);
 
                     let mut err = match level {
-                        Level::Error { lint: false } => sess.struct_err(&msg),
+                        Level::Error { lint: false } => sess.struct_err(&msg).forget_guarantee(),
                         Level::Warning => sess.struct_warn(&msg),
                         Level::Note => sess.struct_note_without_error(&msg),
                         _ => bug!("Invalid inline asm diagnostic level"),
diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs
index ed6c156547e..11f32d92e44 100644
--- a/compiler/rustc_codegen_ssa/src/base.rs
+++ b/compiler/rustc_codegen_ssa/src/base.rs
@@ -407,7 +407,10 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
         // late-bound regions, since late-bound
         // regions must appear in the argument
         // listing.
-        let main_ret_ty = cx.tcx().erase_regions(main_ret_ty.no_bound_vars().unwrap());
+        let main_ret_ty = cx.tcx().normalize_erasing_regions(
+            ty::ParamEnv::reveal_all(),
+            main_ret_ty.no_bound_vars().unwrap(),
+        );
 
         let Some(llfn) = cx.declare_c_main(llfty) else {
             // FIXME: We should be smart and show a better diagnostic here.
diff --git a/compiler/rustc_const_eval/src/const_eval/error.rs b/compiler/rustc_const_eval/src/const_eval/error.rs
index 89a0f8245e5..3bd092263c1 100644
--- a/compiler/rustc_const_eval/src/const_eval/error.rs
+++ b/compiler/rustc_const_eval/src/const_eval/error.rs
@@ -1,7 +1,7 @@
 use std::error::Error;
 use std::fmt;
 
-use rustc_errors::{DiagnosticBuilder, ErrorReported};
+use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_middle::mir::AssertKind;
 use rustc_middle::ty::{layout::LayoutError, query::TyCtxtAt, ConstInt};
@@ -94,13 +94,13 @@ impl<'tcx> ConstEvalErr<'tcx> {
         &self,
         tcx: TyCtxtAt<'tcx>,
         message: &str,
-        emit: impl FnOnce(DiagnosticBuilder<'_>),
+        decorate: impl FnOnce(&mut Diagnostic),
     ) -> ErrorHandled {
-        self.struct_generic(tcx, message, emit, None)
+        self.struct_generic(tcx, message, decorate, None)
     }
 
     pub fn report_as_error(&self, tcx: TyCtxtAt<'tcx>, message: &str) -> ErrorHandled {
-        self.struct_error(tcx, message, |mut e| e.emit())
+        self.struct_error(tcx, message, |_| {})
     }
 
     pub fn report_as_lint(
@@ -113,7 +113,7 @@ impl<'tcx> ConstEvalErr<'tcx> {
         self.struct_generic(
             tcx,
             message,
-            |mut lint: DiagnosticBuilder<'_>| {
+            |lint: &mut Diagnostic| {
                 // Apply the span.
                 if let Some(span) = span {
                     let primary_spans = lint.span.primary_spans().to_vec();
@@ -127,7 +127,6 @@ impl<'tcx> ConstEvalErr<'tcx> {
                         }
                     }
                 }
-                lint.emit();
             },
             Some(lint_root),
         )
@@ -136,9 +135,8 @@ impl<'tcx> ConstEvalErr<'tcx> {
     /// Create a diagnostic for this const eval error.
     ///
     /// Sets the message passed in via `message` and adds span labels with detailed error
-    /// information before handing control back to `emit` to do any final processing.
-    /// It's the caller's responsibility to call emit(), stash(), etc. within the `emit`
-    /// function to dispose of the diagnostic properly.
+    /// information before handing control back to `decorate` to do any final annotations,
+    /// after which the diagnostic is emitted.
     ///
     /// If `lint_root.is_some()` report it as a lint, else report it as a hard error.
     /// (Except that for some errors, we ignore all that -- see `must_error` below.)
@@ -146,10 +144,10 @@ impl<'tcx> ConstEvalErr<'tcx> {
         &self,
         tcx: TyCtxtAt<'tcx>,
         message: &str,
-        emit: impl FnOnce(DiagnosticBuilder<'_>),
+        decorate: impl FnOnce(&mut Diagnostic),
         lint_root: Option<hir::HirId>,
     ) -> ErrorHandled {
-        let finish = |mut err: DiagnosticBuilder<'_>, span_msg: Option<String>| {
+        let finish = |err: &mut Diagnostic, span_msg: Option<String>| {
             trace!("reporting const eval failure at {:?}", self.span);
             if let Some(span_msg) = span_msg {
                 err.span_label(self.span, span_msg);
@@ -188,8 +186,8 @@ impl<'tcx> ConstEvalErr<'tcx> {
                 }
                 flush_last_line(last_frame, times);
             }
-            // Let the caller finish the job.
-            emit(err)
+            // Let the caller attach any additional information it wants.
+            decorate(err);
         };
 
         // Special handling for certain errors
@@ -206,8 +204,9 @@ impl<'tcx> ConstEvalErr<'tcx> {
                 // The `message` makes little sense here, this is a more serious error than the
                 // caller thinks anyway.
                 // See <https://github.com/rust-lang/rust/pull/63152>.
-                finish(struct_error(tcx, &self.error.to_string()), None);
-                return ErrorHandled::Reported(ErrorReported);
+                let mut err = struct_error(tcx, &self.error.to_string());
+                finish(&mut err, None);
+                return ErrorHandled::Reported(err.emit());
             }
             _ => {}
         };
@@ -223,13 +222,18 @@ impl<'tcx> ConstEvalErr<'tcx> {
                 rustc_session::lint::builtin::CONST_ERR,
                 hir_id,
                 tcx.span,
-                |lint| finish(lint.build(message), Some(err_msg)),
+                |lint| {
+                    let mut lint = lint.build(message);
+                    finish(&mut lint, Some(err_msg));
+                    lint.emit();
+                },
             );
             ErrorHandled::Linted
         } else {
             // Report as hard error.
-            finish(struct_error(tcx, message), Some(err_msg));
-            ErrorHandled::Reported(ErrorReported)
+            let mut err = struct_error(tcx, message);
+            finish(&mut err, Some(err_msg));
+            ErrorHandled::Reported(err.emit())
         }
     }
 }
diff --git a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
index 533c32f807d..dad57274104 100644
--- a/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
+++ b/compiler/rustc_const_eval/src/const_eval/eval_queries.rs
@@ -361,7 +361,7 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
                 Err(err.struct_error(
                     ecx.tcx,
                     "it is undefined behavior to use this value",
-                    |mut diag| {
+                    |diag| {
                         diag.note(note_on_undefined_behavior_error());
                         diag.note(&format!(
                             "the raw bytes of the constant ({}",
@@ -370,7 +370,6 @@ pub fn eval_to_allocation_raw_provider<'tcx>(
                                 ecx.tcx.global_alloc(alloc_id).unwrap_memory()
                             )
                         ));
-                        diag.emit();
                     },
                 ))
             } else {
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 888c4b997dc..5738b38d443 100644
--- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs
@@ -1,6 +1,6 @@
 //! Concrete error types for all operations which may be invalid in a certain const context.
 
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::TyCtxtInferExt;
@@ -47,7 +47,11 @@ pub trait NonConstOp<'tcx>: std::fmt::Debug {
         DiagnosticImportance::Primary
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx>;
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported>;
 }
 
 #[derive(Debug)]
@@ -61,7 +65,11 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
         }
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_fn_floating_point_arithmetic,
@@ -75,7 +83,11 @@ impl<'tcx> NonConstOp<'tcx> for FloatingPointOp {
 #[derive(Debug)]
 pub struct FnCallIndirect;
 impl<'tcx> NonConstOp<'tcx> for FnCallIndirect {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         ccx.tcx.sess.struct_span_err(span, "function pointers are not allowed in const fn")
     }
 }
@@ -91,11 +103,15 @@ pub struct FnCallNonConst<'tcx> {
 }
 
 impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        _: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let FnCallNonConst { caller, callee, substs, span, from_hir_call } = *self;
         let ConstCx { tcx, param_env, .. } = *ccx;
 
-        let diag_trait = |mut err, self_ty: Ty<'_>, trait_id| {
+        let diag_trait = |err, self_ty: Ty<'_>, trait_id| {
             let trait_ref = TraitRef::from_method(tcx, trait_id, substs);
 
             match self_ty.kind() {
@@ -115,7 +131,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                         suggest_constraining_type_param(
                             tcx,
                             generics,
-                            &mut err,
+                            err,
                             &param_ty.name.as_str(),
                             &constraint,
                             None,
@@ -146,8 +162,6 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                 }
                 _ => {}
             }
-
-            err
         };
 
         let call_kind = call_kind(tcx, ccx.param_env, callee, substs, span, from_hir_call, None);
@@ -162,7 +176,7 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                     };
                 }
 
-                let err = match kind {
+                let mut err = match kind {
                     CallDesugaringKind::ForLoopIntoIter => {
                         error!("cannot convert `{}` into an iterator in {}s")
                     }
@@ -177,7 +191,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                     }
                 };
 
-                diag_trait(err, self_ty, kind.trait_def_id(tcx))
+                diag_trait(&mut err, self_ty, kind.trait_def_id(tcx));
+                err
             }
             CallKind::FnCall { fn_trait_id, self_ty } => {
                 let mut err = struct_span_err!(
@@ -212,7 +227,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                     _ => {}
                 }
 
-                diag_trait(err, self_ty, fn_trait_id)
+                diag_trait(&mut err, self_ty, fn_trait_id);
+                err
             }
             CallKind::Operator { trait_id, self_ty, .. } => {
                 let mut err = struct_span_err!(
@@ -262,7 +278,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                     }
                 }
 
-                diag_trait(err, self_ty, trait_id)
+                diag_trait(&mut err, self_ty, trait_id);
+                err
             }
             CallKind::DerefCoercion { deref_target, deref_target_ty, self_ty } => {
                 let mut err = struct_span_err!(
@@ -281,7 +298,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
                     err.span_note(deref_target, "deref defined here");
                 }
 
-                diag_trait(err, self_ty, tcx.lang_items().deref_trait().unwrap())
+                diag_trait(&mut err, self_ty, tcx.lang_items().deref_trait().unwrap());
+                err
             }
             _ => struct_span_err!(
                 ccx.tcx.sess,
@@ -310,7 +328,11 @@ impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> {
 pub struct FnCallUnstable(pub DefId, pub Option<Symbol>);
 
 impl<'tcx> NonConstOp<'tcx> for FnCallUnstable {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let FnCallUnstable(def_id, feature) = *self;
 
         let mut err = ccx.tcx.sess.struct_span_err(
@@ -344,7 +366,11 @@ impl<'tcx> NonConstOp<'tcx> for FnPtrCast {
         }
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_fn_fn_ptr_basics,
@@ -365,7 +391,11 @@ impl<'tcx> NonConstOp<'tcx> for Generator {
         }
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         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)
@@ -378,7 +408,11 @@ impl<'tcx> NonConstOp<'tcx> for Generator {
 #[derive(Debug)]
 pub struct HeapAllocation;
 impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -402,7 +436,11 @@ impl<'tcx> NonConstOp<'tcx> for HeapAllocation {
 #[derive(Debug)]
 pub struct InlineAsm;
 impl<'tcx> NonConstOp<'tcx> for InlineAsm {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -418,7 +456,11 @@ pub struct LiveDrop {
     pub dropped_at: Option<Span>,
 }
 impl<'tcx> NonConstOp<'tcx> for LiveDrop {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -446,7 +488,11 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
         // not additionally emit a feature gate error if activating the feature gate won't work.
         DiagnosticImportance::Secondary
     }
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_refs_to_cell,
@@ -462,7 +508,11 @@ impl<'tcx> NonConstOp<'tcx> for TransientCellBorrow {
 /// it in the future for static items.
 pub struct CellBorrow;
 impl<'tcx> NonConstOp<'tcx> for CellBorrow {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -509,7 +559,11 @@ impl<'tcx> NonConstOp<'tcx> for MutBorrow {
         DiagnosticImportance::Secondary
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let raw = match self.0 {
             hir::BorrowKind::Raw => "raw ",
             hir::BorrowKind::Ref => "",
@@ -548,7 +602,11 @@ impl<'tcx> NonConstOp<'tcx> for TransientMutBorrow {
         Status::Unstable(sym::const_mut_refs)
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let raw = match self.0 {
             hir::BorrowKind::Raw => "raw ",
             hir::BorrowKind::Ref => "",
@@ -575,7 +633,11 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref {
         DiagnosticImportance::Secondary
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_mut_refs,
@@ -589,7 +651,11 @@ impl<'tcx> NonConstOp<'tcx> for MutDeref {
 #[derive(Debug)]
 pub struct PanicNonStr;
 impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         ccx.tcx.sess.struct_span_err(
             span,
             "argument to `panic!()` in a const context must have type `&str`",
@@ -603,7 +669,11 @@ impl<'tcx> NonConstOp<'tcx> for PanicNonStr {
 #[derive(Debug)]
 pub struct RawPtrComparison;
 impl<'tcx> NonConstOp<'tcx> for RawPtrComparison {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = ccx
             .tcx
             .sess
@@ -623,7 +693,11 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
         Status::Unstable(sym::const_mut_refs)
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         feature_err(
             &ccx.tcx.sess.parse_sess,
             sym::const_mut_refs,
@@ -639,7 +713,11 @@ impl<'tcx> NonConstOp<'tcx> for RawMutPtrDeref {
 #[derive(Debug)]
 pub struct RawPtrToIntCast;
 impl<'tcx> NonConstOp<'tcx> for RawPtrToIntCast {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = ccx
             .tcx
             .sess
@@ -664,7 +742,11 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
         }
     }
 
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -690,7 +772,11 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess {
 #[derive(Debug)]
 pub struct ThreadLocalAccess;
 impl<'tcx> NonConstOp<'tcx> for ThreadLocalAccess {
-    fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+    fn build_error(
+        &self,
+        ccx: &ConstCx<'_, 'tcx>,
+        span: Span,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         struct_span_err!(
             ccx.tcx.sess,
             span,
@@ -721,7 +807,11 @@ pub mod ty {
             }
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_mut_refs,
@@ -751,7 +841,11 @@ pub mod ty {
             }
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_fn_fn_ptr_basics,
@@ -768,7 +862,11 @@ pub mod ty {
             Status::Unstable(sym::const_impl_trait)
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_impl_trait,
@@ -798,7 +896,11 @@ pub mod ty {
             }
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             let mut err = feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_fn_trait_bound,
@@ -837,7 +939,11 @@ pub mod ty {
             }
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             let mut err = feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_fn_trait_bound,
@@ -864,7 +970,11 @@ pub mod ty {
             Status::Unstable(sym::const_trait_bound_opt_out)
         }
 
-        fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> DiagnosticBuilder<'tcx> {
+        fn build_error(
+            &self,
+            ccx: &ConstCx<'_, 'tcx>,
+            span: Span,
+        ) -> DiagnosticBuilder<'tcx, ErrorReported> {
             feature_err(
                 &ccx.tcx.sess.parse_sess,
                 sym::const_trait_bound_opt_out,
diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
index 9db8f751390..7d7ab1ed4e5 100644
--- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
+++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs
@@ -66,12 +66,14 @@ fn source_string(file: Lrc<SourceFile>, line: &Line) -> String {
 /// Maps `Diagnostic::Level` to `snippet::AnnotationType`
 fn annotation_type_for_level(level: Level) -> AnnotationType {
     match level {
-        Level::Bug | Level::Fatal | Level::Error { .. } => AnnotationType::Error,
+        Level::Bug | Level::DelayedBug | Level::Fatal | Level::Error { .. } => {
+            AnnotationType::Error
+        }
         Level::Warning => AnnotationType::Warning,
         Level::Note => AnnotationType::Note,
         Level::Help => AnnotationType::Help,
-        // FIXME(#59346): Not sure how to map these two levels
-        Level::Cancelled | Level::FailureNote => AnnotationType::Error,
+        // FIXME(#59346): Not sure how to map this level
+        Level::FailureNote => AnnotationType::Error,
         Level::Allow => panic!("Should not call with Allow"),
     }
 }
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 8cfecafd20c..6d6ada86428 100644
--- a/compiler/rustc_errors/src/diagnostic.rs
+++ b/compiler/rustc_errors/src/diagnostic.rs
@@ -19,7 +19,10 @@ pub struct SuggestionsDisabled;
 #[must_use]
 #[derive(Clone, Debug, Encodable, Decodable)]
 pub struct Diagnostic {
-    pub level: Level,
+    // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
+    // outside of what methods in this crate themselves allow.
+    crate level: Level,
+
     pub message: Vec<(String, Style)>,
     pub code: Option<DiagnosticId>,
     pub span: MultiSpan,
@@ -117,11 +120,20 @@ impl Diagnostic {
         }
     }
 
+    #[inline(always)]
+    pub fn level(&self) -> Level {
+        self.level
+    }
+
     pub fn is_error(&self) -> bool {
         match self.level {
-            Level::Bug | Level::Fatal | Level::Error { .. } | Level::FailureNote => true,
+            Level::Bug
+            | Level::DelayedBug
+            | Level::Fatal
+            | Level::Error { .. }
+            | Level::FailureNote => true,
 
-            Level::Warning | Level::Note | Level::Help | Level::Cancelled | Level::Allow => false,
+            Level::Warning | Level::Note | Level::Help | Level::Allow => false,
         }
     }
 
@@ -139,15 +151,26 @@ impl Diagnostic {
         }
     }
 
-    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
-    /// canceled or it will panic when dropped).
-    pub fn cancel(&mut self) {
-        self.level = Level::Cancelled;
-    }
+    /// Delay emission of this diagnostic as a bug.
+    ///
+    /// This can be useful in contexts where an error indicates a bug but
+    /// typically this only happens when other compilation errors have already
+    /// happened. In those cases this can be used to defer emission of this
+    /// diagnostic as a bug in the compiler only if no other errors have been
+    /// emitted.
+    ///
+    /// In the meantime, though, callsites are required to deal with the "bug"
+    /// locally in whichever way makes the most sense.
+    #[track_caller]
+    pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
+        assert!(
+            self.is_error(),
+            "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
+            self.level
+        );
+        self.level = Level::DelayedBug;
 
-    /// Check if this diagnostic [was cancelled][Self::cancel()].
-    pub fn cancelled(&self) -> bool {
-        self.level == Level::Cancelled
+        self
     }
 
     /// Adds a span/label to be included in the resulting snippet.
@@ -163,6 +186,20 @@ impl Diagnostic {
         self
     }
 
+    /// Labels all the given spans with the provided label.
+    /// See [`Self::span_label()`] for more information.
+    pub fn span_labels(
+        &mut self,
+        spans: impl IntoIterator<Item = Span>,
+        label: impl AsRef<str>,
+    ) -> &mut Self {
+        let label = label.as_ref();
+        for span in spans {
+            self.span_label(span, label);
+        }
+        self
+    }
+
     pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
         let before = self.span.clone();
         self.set_span(after);
@@ -174,7 +211,7 @@ impl Diagnostic {
         self
     }
 
-    crate fn note_expected_found(
+    pub fn note_expected_found(
         &mut self,
         expected_label: &dyn fmt::Display,
         expected: DiagnosticStyledString,
@@ -184,7 +221,7 @@ impl Diagnostic {
         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
     }
 
-    crate fn note_unsuccessful_coercion(
+    pub fn note_unsuccessful_coercion(
         &mut self,
         expected: DiagnosticStyledString,
         found: DiagnosticStyledString,
@@ -274,33 +311,33 @@ impl Diagnostic {
 
     /// Prints the span with a note above it.
     /// This is like [`Diagnostic::note()`], but it gets its own span.
-    crate fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
+    pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
         self.sub(Level::Note, msg, sp.into(), None);
         self
     }
 
     /// Add a warning attached to this diagnostic.
-    crate fn warn(&mut self, msg: &str) -> &mut Self {
+    pub fn warn(&mut self, msg: &str) -> &mut Self {
         self.sub(Level::Warning, msg, MultiSpan::new(), None);
         self
     }
 
     /// Prints the span with a warning above it.
     /// This is like [`Diagnostic::warn()`], but it gets its own span.
-    crate fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
+    pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
         self.sub(Level::Warning, msg, sp.into(), None);
         self
     }
 
     /// Add a help message attached to this diagnostic.
-    crate fn help(&mut self, msg: &str) -> &mut Self {
+    pub fn help(&mut self, msg: &str) -> &mut Self {
         self.sub(Level::Help, msg, MultiSpan::new(), None);
         self
     }
 
     /// Prints the span with some help above it.
     /// This is like [`Diagnostic::help()`], but it gets its own span.
-    crate fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
+    pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
         self.sub(Level::Help, msg, sp.into(), None);
         self
     }
@@ -634,7 +671,7 @@ impl Diagnostic {
         self.code.clone()
     }
 
-    crate fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
+    pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
         self.message[0] = (msg.into(), Style::NoStyle);
         self
     }
diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs
index 3c8751a7a35..49305d22684 100644
--- a/compiler/rustc_errors/src/diagnostic_builder.rs
+++ b/compiler/rustc_errors/src/diagnostic_builder.rs
@@ -1,9 +1,10 @@
-use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString};
+use crate::{Diagnostic, DiagnosticId, DiagnosticStyledString, ErrorReported};
 use crate::{Handler, Level, StashKey};
 use rustc_lint_defs::Applicability;
 
 use rustc_span::{MultiSpan, Span};
 use std::fmt::{self, Debug};
+use std::marker::PhantomData;
 use std::ops::{Deref, DerefMut};
 use std::thread::panicking;
 use tracing::debug;
@@ -15,8 +16,25 @@ use tracing::debug;
 /// extending `HandlerFlags`, accessed via `self.handler.flags`.
 #[must_use]
 #[derive(Clone)]
-pub struct DiagnosticBuilder<'a> {
-    handler: &'a Handler,
+pub struct DiagnosticBuilder<'a, G: EmissionGuarantee> {
+    inner: DiagnosticBuilderInner<'a>,
+    _marker: PhantomData<G>,
+}
+
+/// This type exists only for `DiagnosticBuilder::forget_guarantee`, because it:
+/// 1. lacks the `G` parameter and therefore `DiagnosticBuilder<G1>` can be
+///    converted into `DiagnosticBuilder<G2>` while reusing the `inner` field
+/// 2. can implement the `Drop` "bomb" instead of `DiagnosticBuilder`, as it
+///    contains all of the data (`state` + `diagnostic`) of `DiagnosticBuilder`
+///
+/// The `diagnostic` field is not `Copy` and can't be moved out of whichever
+/// type implements the `Drop` "bomb", but because of the above two facts, that
+/// never needs to happen - instead, the whole `inner: DiagnosticBuilderInner`
+/// can be moved out of a `DiagnosticBuilder` and into another.
+#[must_use]
+#[derive(Clone)]
+struct DiagnosticBuilderInner<'a> {
+    state: DiagnosticBuilderState<'a>,
 
     /// `Diagnostic` is a large type, and `DiagnosticBuilder` is often used as a
     /// return value, especially within the frequently-used `PResult` type.
@@ -25,6 +43,161 @@ pub struct DiagnosticBuilder<'a> {
     diagnostic: Box<Diagnostic>,
 }
 
+#[derive(Clone)]
+enum DiagnosticBuilderState<'a> {
+    /// Initial state of a `DiagnosticBuilder`, before `.emit()` or `.cancel()`.
+    ///
+    /// The `Diagnostic` will be emitted through this `Handler`.
+    Emittable(&'a Handler),
+
+    /// State of a `DiagnosticBuilder`, after `.emit()` or *during* `.cancel()`.
+    ///
+    /// The `Diagnostic` will be ignored when calling `.emit()`, and it can be
+    /// assumed that `.emit()` was previously called, to end up in this state.
+    ///
+    /// While this is also used by `.cancel()`, this state is only observed by
+    /// the `Drop` `impl` of `DiagnosticBuilderInner`, as `.cancel()` takes
+    /// `self` by-value specifically to prevent any attempts to `.emit()`.
+    ///
+    // FIXME(eddyb) currently this doesn't prevent extending the `Diagnostic`,
+    // despite that being potentially lossy, if important information is added
+    // *after* the original `.emit()` call.
+    AlreadyEmittedOrDuringCancellation,
+}
+
+// `DiagnosticBuilderState` should be pointer-sized.
+rustc_data_structures::static_assert_size!(
+    DiagnosticBuilderState<'_>,
+    std::mem::size_of::<&Handler>()
+);
+
+/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee"
+/// (or "proof") token that the emission happened.
+pub trait EmissionGuarantee: Sized {
+    /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each
+    /// `impl` of `EmissionGuarantee`, to make it impossible to create a value
+    /// of `Self` without actually performing the emission.
+    #[track_caller]
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self;
+}
+
+/// Private module for sealing the `IsError` helper trait.
+mod sealed_level_is_error {
+    use crate::Level;
+
+    /// Sealed helper trait for statically checking that a `Level` is an error.
+    crate trait IsError<const L: Level> {}
+
+    impl IsError<{ Level::Bug }> for () {}
+    impl IsError<{ Level::DelayedBug }> for () {}
+    impl IsError<{ Level::Fatal }> for () {}
+    // NOTE(eddyb) `Level::Error { lint: true }` is also an error, but lints
+    // don't need error guarantees, as their levels are always dynamic.
+    impl IsError<{ Level::Error { lint: false } }> for () {}
+}
+
+impl<'a> DiagnosticBuilder<'a, ErrorReported> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    crate fn new_guaranteeing_error<const L: Level>(handler: &'a Handler, message: &str) -> Self
+    where
+        (): sealed_level_is_error::IsError<L>,
+    {
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(Diagnostic::new_with_code(L, None, message)),
+            },
+            _marker: PhantomData,
+        }
+    }
+
+    /// Discard the guarantee `.emit()` would return, in favor of having the
+    /// type `DiagnosticBuilder<'a, ()>`. This may be necessary whenever there
+    /// is a common codepath handling both errors and warnings.
+    pub fn forget_guarantee(self) -> DiagnosticBuilder<'a, ()> {
+        DiagnosticBuilder { inner: self.inner, _marker: PhantomData }
+    }
+}
+
+// FIXME(eddyb) make `ErrorReported` impossible to create outside `.emit()`.
+impl EmissionGuarantee for ErrorReported {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                handler.emit_diagnostic(&db.inner.diagnostic);
+
+                // Only allow a guarantee if the `level` wasn't switched to a
+                // non-error - the field isn't `pub`, but the whole `Diagnostic`
+                // can be overwritten with a new one, thanks to `DerefMut`.
+                assert!(
+                    db.inner.diagnostic.is_error(),
+                    "emitted non-error ({:?}) diagnostic \
+                     from `DiagnosticBuilder<ErrorReported>`",
+                    db.inner.diagnostic.level,
+                );
+                ErrorReported
+            }
+            // `.emit()` was previously called, disallowed from repeating it,
+            // but can take advantage of the previous `.emit()`'s guarantee
+            // still being applicable (i.e. as a form of idempotency).
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
+                // Only allow a guarantee if the `level` wasn't switched to a
+                // non-error - the field isn't `pub`, but the whole `Diagnostic`
+                // can be overwritten with a new one, thanks to `DerefMut`.
+                assert!(
+                    db.inner.diagnostic.is_error(),
+                    "`DiagnosticBuilder<ErrorReported>`'s diagnostic \
+                     became non-error ({:?}), after original `.emit()`",
+                    db.inner.diagnostic.level,
+                );
+                ErrorReported
+            }
+        }
+    }
+}
+
+impl<'a> DiagnosticBuilder<'a, ()> {
+    /// Convenience function for internal use, clients should use one of the
+    /// `struct_*` methods on [`Handler`].
+    crate fn new(handler: &'a Handler, level: Level, message: &str) -> Self {
+        let diagnostic = Diagnostic::new_with_code(level, None, message);
+        Self::new_diagnostic(handler, diagnostic)
+    }
+
+    /// Creates a new `DiagnosticBuilder` with an already constructed
+    /// diagnostic.
+    crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> Self {
+        debug!("Created new diagnostic");
+        Self {
+            inner: DiagnosticBuilderInner {
+                state: DiagnosticBuilderState::Emittable(handler),
+                diagnostic: Box::new(diagnostic),
+            },
+            _marker: PhantomData,
+        }
+    }
+}
+
+// FIXME(eddyb) should there be a `Option<ErrorReported>` impl as well?
+impl EmissionGuarantee for () {
+    fn diagnostic_builder_emit_producing_guarantee(db: &mut DiagnosticBuilder<'_, Self>) -> Self {
+        match db.inner.state {
+            // First `.emit()` call, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => {
+                db.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+
+                handler.emit_diagnostic(&db.inner.diagnostic);
+            }
+            // `.emit()` was previously called, disallowed from repeating it.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
+        }
+    }
+}
+
 /// In general, the `DiagnosticBuilder` uses deref to allow access to
 /// the fields and methods of the embedded `diagnostic` in a
 /// transparent way. *However,* many of the methods are intended to
@@ -55,60 +228,54 @@ macro_rules! forward {
         $(#[$attrs])*
         #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
         pub fn $n(&mut self, $($name: $ty),*) -> &mut Self {
-            self.diagnostic.$n($($name),*);
-            self
-        }
-    };
-
-    // Forward pattern for &mut self -> &mut Self, with generic parameters.
-    (
-        $(#[$attrs:meta])*
-        pub fn $n:ident<$($generic:ident: $bound:path),*>(
-            &mut self,
-            $($name:ident: $ty:ty),*
-            $(,)?
-        ) -> &mut Self
-    ) => {
-        $(#[$attrs])*
-        #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
-        pub fn $n<$($generic: $bound),*>(&mut self, $($name: $ty),*) -> &mut Self {
-            self.diagnostic.$n($($name),*);
+            self.inner.diagnostic.$n($($name),*);
             self
         }
     };
 }
 
-impl<'a> Deref for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> Deref for DiagnosticBuilder<'_, G> {
     type Target = Diagnostic;
 
     fn deref(&self) -> &Diagnostic {
-        &self.diagnostic
+        &self.inner.diagnostic
     }
 }
 
-impl<'a> DerefMut for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> DerefMut for DiagnosticBuilder<'_, G> {
     fn deref_mut(&mut self) -> &mut Diagnostic {
-        &mut self.diagnostic
+        &mut self.inner.diagnostic
     }
 }
 
-impl<'a> DiagnosticBuilder<'a> {
+impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
     /// Emit the diagnostic.
-    pub fn emit(&mut self) {
-        self.handler.emit_diagnostic(&self);
-        self.cancel();
+    #[track_caller]
+    pub fn emit(&mut self) -> G {
+        G::diagnostic_builder_emit_producing_guarantee(self)
     }
 
     /// Emit the diagnostic unless `delay` is true,
     /// in which case the emission will be delayed as a bug.
     ///
     /// See `emit` and `delay_as_bug` for details.
-    pub fn emit_unless(&mut self, delay: bool) {
+    #[track_caller]
+    pub fn emit_unless(&mut self, delay: bool) -> G {
         if delay {
-            self.delay_as_bug();
-        } else {
-            self.emit();
+            self.downgrade_to_delayed_bug();
         }
+        self.emit()
+    }
+
+    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
+    /// cancelled or it will panic when dropped).
+    ///
+    /// This method takes `self` by-value to disallow calling `.emit()` on it,
+    /// which may be expected to *guarantee* the emission of an error, either
+    /// at the time of the call, or through a prior `.emit()` call.
+    pub fn cancel(mut self) {
+        self.inner.state = DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation;
+        drop(self);
     }
 
     /// Stashes diagnostic for possible later improvement in a different,
@@ -123,21 +290,28 @@ impl<'a> DiagnosticBuilder<'a> {
     }
 
     /// Converts the builder to a `Diagnostic` for later emission,
-    /// unless handler has disabled such buffering.
+    /// unless handler has disabled such buffering, or `.emit()` was called.
     pub fn into_diagnostic(mut self) -> Option<(Diagnostic, &'a Handler)> {
-        if self.handler.flags.dont_buffer_diagnostics
-            || self.handler.flags.treat_err_as_bug.is_some()
-        {
+        let handler = match self.inner.state {
+            // No `.emit()` calls, the `&Handler` is still available.
+            DiagnosticBuilderState::Emittable(handler) => handler,
+            // `.emit()` was previously called, nothing we can do.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {
+                return None;
+            }
+        };
+
+        if handler.flags.dont_buffer_diagnostics || handler.flags.treat_err_as_bug.is_some() {
             self.emit();
             return None;
         }
 
-        let handler = self.handler;
+        // Take the `Diagnostic` by replacing it with a dummy.
+        let dummy = Diagnostic::new(Level::Allow, "");
+        let diagnostic = std::mem::replace(&mut *self.inner.diagnostic, dummy);
 
-        // We must use `Level::Cancelled` for `dummy` to avoid an ICE about an
-        // unused diagnostic.
-        let dummy = Diagnostic::new(Level::Cancelled, "");
-        let diagnostic = std::mem::replace(&mut *self.diagnostic, dummy);
+        // Disable the ICE on `Drop`.
+        self.cancel();
 
         // Logging here is useful to help track down where in logs an error was
         // actually emitted.
@@ -162,12 +336,18 @@ impl<'a> DiagnosticBuilder<'a> {
     ///
     /// In the meantime, though, callsites are required to deal with the "bug"
     /// locally in whichever way makes the most sense.
+    #[track_caller]
     pub fn delay_as_bug(&mut self) {
-        self.level = Level::Bug;
-        self.handler.delay_as_bug((*self.diagnostic).clone());
-        self.cancel();
+        self.downgrade_to_delayed_bug();
+        self.emit();
     }
 
+    forward!(
+        #[track_caller]
+        pub fn downgrade_to_delayed_bug(&mut self,) -> &mut Self
+    );
+
+    forward!(
     /// Appends a labeled span to the diagnostic.
     ///
     /// Labels are used to convey additional context for the diagnostic's primary span. They will
@@ -180,24 +360,16 @@ impl<'a> DiagnosticBuilder<'a> {
     /// the diagnostic was constructed. However, the label span is *not* considered a
     /// ["primary span"][`MultiSpan`]; only the `Span` supplied when creating the diagnostic is
     /// primary.
-    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self {
-        self.diagnostic.span_label(span, label);
-        self
-    }
+    pub fn span_label(&mut self, span: Span, label: impl Into<String>) -> &mut Self);
 
+    forward!(
     /// Labels all the given spans with the provided label.
     /// See [`Diagnostic::span_label()`] for more information.
     pub fn span_labels(
         &mut self,
         spans: impl IntoIterator<Item = Span>,
         label: impl AsRef<str>,
-    ) -> &mut Self {
-        let label = label.as_ref();
-        for span in spans {
-            self.diagnostic.span_label(span, label);
-        }
-        self
-    }
+    ) -> &mut Self);
 
     forward!(pub fn note_expected_found(
         &mut self,
@@ -224,17 +396,17 @@ impl<'a> DiagnosticBuilder<'a> {
     ) -> &mut Self);
 
     forward!(pub fn note(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_note<S: Into<MultiSpan>>(
+    forward!(pub fn span_note(
         &mut self,
-        sp: S,
+        sp: impl Into<MultiSpan>,
         msg: &str,
     ) -> &mut Self);
     forward!(pub fn warn(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self);
+    forward!(pub fn span_warn(&mut self, sp: impl Into<MultiSpan>, msg: &str) -> &mut Self);
     forward!(pub fn help(&mut self, msg: &str) -> &mut Self);
-    forward!(pub fn span_help<S: Into<MultiSpan>>(
+    forward!(pub fn span_help(
         &mut self,
-        sp: S,
+        sp: impl Into<MultiSpan>,
         msg: &str,
     ) -> &mut Self);
     forward!(pub fn set_is_lint(&mut self,) -> &mut Self);
@@ -308,55 +480,35 @@ impl<'a> DiagnosticBuilder<'a> {
         applicability: Applicability,
     ) -> &mut Self);
 
-    forward!(pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self);
-    forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
+    forward!(pub fn set_primary_message(&mut self, msg: impl Into<String>) -> &mut Self);
+    forward!(pub fn set_span(&mut self, sp: impl Into<MultiSpan>) -> &mut Self);
     forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
-
-    /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`Handler`].
-    crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
-        DiagnosticBuilder::new_with_code(handler, level, None, message)
-    }
-
-    /// Convenience function for internal use, clients should use one of the
-    /// `struct_*` methods on [`Handler`].
-    crate fn new_with_code(
-        handler: &'a Handler,
-        level: Level,
-        code: Option<DiagnosticId>,
-        message: &str,
-    ) -> DiagnosticBuilder<'a> {
-        let diagnostic = Diagnostic::new_with_code(level, code, message);
-        DiagnosticBuilder::new_diagnostic(handler, diagnostic)
-    }
-
-    /// Creates a new `DiagnosticBuilder` with an already constructed
-    /// diagnostic.
-    crate fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic) -> DiagnosticBuilder<'a> {
-        debug!("Created new diagnostic");
-        DiagnosticBuilder { handler, diagnostic: Box::new(diagnostic) }
-    }
 }
 
-impl<'a> Debug for DiagnosticBuilder<'a> {
+impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.diagnostic.fmt(f)
+        self.inner.diagnostic.fmt(f)
     }
 }
 
-/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or canceled
+/// Destructor bomb - a `DiagnosticBuilder` must be either emitted or cancelled
 /// or we emit a bug.
-impl<'a> Drop for DiagnosticBuilder<'a> {
+impl Drop for DiagnosticBuilderInner<'_> {
     fn drop(&mut self) {
-        if !panicking() && !self.cancelled() {
-            let mut db = DiagnosticBuilder::new(
-                self.handler,
-                Level::Bug,
-                "the following error was constructed but not emitted",
-            );
-            db.emit();
-            self.emit();
-            panic!();
+        match self.state {
+            // No `.emit()` or `.cancel()` calls.
+            DiagnosticBuilderState::Emittable(handler) => {
+                if !panicking() {
+                    handler.emit_diagnostic(&Diagnostic::new(
+                        Level::Bug,
+                        "the following error was constructed but not emitted",
+                    ));
+                    handler.emit_diagnostic(&self.diagnostic);
+                    panic!();
+                }
+            }
+            // `.emit()` was previously called, or maybe we're during `.cancel()`.
+            DiagnosticBuilderState::AlreadyEmittedOrDuringCancellation => {}
         }
     }
 }
diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs
index ff3478073d9..dc28d1bb452 100644
--- a/compiler/rustc_errors/src/json.rs
+++ b/compiler/rustc_errors/src/json.rs
@@ -454,8 +454,14 @@ impl DiagnosticSpan {
         let end = je.sm.lookup_char_pos(span.hi());
         let backtrace_step = backtrace.next().map(|bt| {
             let call_site = Self::from_span_full(bt.call_site, false, None, None, backtrace, je);
-            let def_site_span =
-                Self::from_span_full(bt.def_site, false, None, None, [].into_iter(), je);
+            let def_site_span = Self::from_span_full(
+                je.sm.guess_head_span(bt.def_site),
+                false,
+                None,
+                None,
+                [].into_iter(),
+                je,
+            );
             Box::new(DiagnosticSpanMacroExpansion {
                 span: call_site,
                 macro_decl_name: bt.kind.descr(),
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index a5c954cca13..463308c27b2 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -9,6 +9,8 @@
 #![feature(let_else)]
 #![feature(nll)]
 #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
+#![feature(adt_const_params)]
+#![allow(incomplete_features)]
 
 #[macro_use]
 extern crate rustc_macros;
@@ -52,7 +54,7 @@ mod snippet;
 mod styled_buffer;
 pub use snippet::Style;
 
-pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a>>;
+pub type PResult<'a, T> = Result<T, DiagnosticBuilder<'a, ErrorReported>>;
 
 // `PResult` is used a lot. Make sure it doesn't unintentionally get bigger.
 // (See also the comment on `DiagnosticBuilder`'s `diagnostic` field.)
@@ -491,10 +493,15 @@ impl Drop for HandlerInner {
             self.flush_delayed(bugs, "no errors encountered even though `delay_span_bug` issued");
         }
 
+        // FIXME(eddyb) this explains what `delayed_good_path_bugs` are!
+        // They're `delayed_span_bugs` but for "require some diagnostic happened"
+        // instead of "require some error happened". Sadly that isn't ideal, as
+        // lints can be `#[allow]`'d, potentially leading to this triggering.
+        // Also, "good path" should be replaced with a better naming.
         if !self.has_any_message() {
             let bugs = std::mem::replace(&mut self.delayed_good_path_bugs, Vec::new());
             self.flush_delayed(
-                bugs.into_iter().map(DelayedDiagnostic::decorate).collect(),
+                bugs.into_iter().map(DelayedDiagnostic::decorate),
                 "no warnings or errors encountered even though `delayed_good_path_bugs` issued",
             );
         }
@@ -604,7 +611,7 @@ impl Handler {
     }
 
     /// Steal a previously stashed diagnostic with the given `Span` and `StashKey` as the key.
-    pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_>> {
+    pub fn steal_diagnostic(&self, span: Span, key: StashKey) -> Option<DiagnosticBuilder<'_, ()>> {
         self.inner
             .borrow_mut()
             .stashed_diagnostics
@@ -617,33 +624,17 @@ impl Handler {
         self.inner.borrow_mut().emit_stashed_diagnostics();
     }
 
-    /// Construct a dummy builder with `Level::Cancelled`.
-    ///
-    /// Using this will neither report anything to the user (e.g. a warning),
-    /// nor will compilation cancel as a result.
-    pub fn struct_dummy(&self) -> DiagnosticBuilder<'_> {
-        DiagnosticBuilder::new(self, Level::Cancelled, "")
-    }
-
-    /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
-    ///
-    /// The builder will be canceled if warnings cannot be emitted.
-    pub fn struct_span_warn(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
-        let mut result = self.struct_warn(msg);
-        result.set_span(span);
-        result
-    }
-
     /// Construct a builder at the `Warning` level at the given `span` and with the `msg`.
     ///
-    /// This will "force" the warning meaning it will not be canceled even
-    /// if warnings cannot be emitted.
-    pub fn struct_span_force_warn(
+    /// Attempting to `.emit()` the builder will only emit if either:
+    /// * `can_emit_warnings` is `true`
+    /// * `is_force_warn` was set in `DiagnosticId::Lint`
+    pub fn struct_span_warn(
         &self,
         span: impl Into<MultiSpan>,
         msg: &str,
-    ) -> DiagnosticBuilder<'_> {
-        let mut result = self.struct_force_warn(msg);
+    ) -> DiagnosticBuilder<'_, ()> {
+        let mut result = self.struct_warn(msg);
         result.set_span(span);
         result
     }
@@ -653,7 +644,7 @@ impl Handler {
         &self,
         span: impl Into<MultiSpan>,
         msg: &str,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ()> {
         let mut result = self.struct_allow(msg);
         result.set_span(span);
         result
@@ -666,7 +657,7 @@ impl Handler {
         span: impl Into<MultiSpan>,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ()> {
         let mut result = self.struct_span_warn(span, msg);
         result.code(code);
         result
@@ -674,30 +665,24 @@ impl Handler {
 
     /// Construct a builder at the `Warning` level with the `msg`.
     ///
-    /// The builder will be canceled if warnings cannot be emitted.
-    pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
-        let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
-        if !self.flags.can_emit_warnings {
-            result.cancel();
-        }
-        result
-    }
-
-    /// Construct a builder at the `Warning` level with the `msg`.
-    ///
-    /// This will "force" a warning meaning it will not be canceled even
-    /// if warnings cannot be emitted.
-    pub fn struct_force_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    /// Attempting to `.emit()` the builder will only emit if either:
+    /// * `can_emit_warnings` is `true`
+    /// * `is_force_warn` was set in `DiagnosticId::Lint`
+    pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Level::Warning, msg)
     }
 
     /// Construct a builder at the `Allow` level with the `msg`.
-    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Level::Allow, msg)
     }
 
     /// Construct a builder at the `Error` level at the given `span` and with the `msg`.
-    pub fn struct_span_err(&self, span: impl Into<MultiSpan>, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_span_err(
+        &self,
+        span: impl Into<MultiSpan>,
+        msg: &str,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let mut result = self.struct_err(msg);
         result.set_span(span);
         result
@@ -709,7 +694,7 @@ impl Handler {
         span: impl Into<MultiSpan>,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let mut result = self.struct_span_err(span, msg);
         result.code(code);
         result
@@ -717,18 +702,22 @@ impl Handler {
 
     /// Construct a builder at the `Error` level with the `msg`.
     // FIXME: This method should be removed (every error should have an associated error code).
-    pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> {
-        DiagnosticBuilder::new(self, Level::Error { lint: false }, msg)
+    pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
+        DiagnosticBuilder::new_guaranteeing_error::<{ Level::Error { lint: false } }>(self, msg)
     }
 
     /// This should only be used by `rustc_middle::lint::struct_lint_level`. Do not use it for hard errors.
     #[doc(hidden)]
-    pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_err_lint(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Level::Error { lint: true }, msg)
     }
 
     /// Construct a builder at the `Error` level with the `msg` and the `code`.
-    pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> {
+    pub fn struct_err_with_code(
+        &self,
+        msg: &str,
+        code: DiagnosticId,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let mut result = self.struct_err(msg);
         result.code(code);
         result
@@ -739,7 +728,7 @@ impl Handler {
         &self,
         span: impl Into<MultiSpan>,
         msg: &str,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let mut result = self.struct_fatal(msg);
         result.set_span(span);
         result
@@ -751,24 +740,24 @@ impl Handler {
         span: impl Into<MultiSpan>,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let mut result = self.struct_span_fatal(span, msg);
         result.code(code);
         result
     }
 
     /// Construct a builder at the `Error` level with the `msg`.
-    pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
-        DiagnosticBuilder::new(self, Level::Fatal, msg)
+    pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
+        DiagnosticBuilder::new_guaranteeing_error::<{ Level::Fatal }>(self, msg)
     }
 
     /// Construct a builder at the `Help` level with the `msg`.
-    pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_help(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Level::Help, msg)
     }
 
     /// Construct a builder at the `Note` level with the `msg`.
-    pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         DiagnosticBuilder::new(self, Level::Note, msg)
     }
 
@@ -815,6 +804,8 @@ impl Handler {
         self.inner.borrow_mut().delay_span_bug(span, msg)
     }
 
+    // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
+    // where the explanation of what "good path" is (also, it should be renamed).
     pub fn delay_good_path_bug(&self, msg: &str) {
         self.inner.borrow_mut().delay_good_path_bug(msg)
     }
@@ -827,7 +818,7 @@ impl Handler {
         self.emit_diag_at_span(Diagnostic::new(Note, msg), span);
     }
 
-    pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn span_note_diag(&self, span: Span, msg: &str) -> DiagnosticBuilder<'_, ()> {
         let mut db = DiagnosticBuilder::new(self, Note, msg);
         db.set_span(span);
         db
@@ -915,10 +906,6 @@ impl Handler {
     pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
         self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
     }
-
-    pub fn delay_as_bug(&self, diagnostic: Diagnostic) {
-        self.inner.borrow_mut().delay_as_bug(diagnostic)
-    }
 }
 
 impl HandlerInner {
@@ -936,9 +923,18 @@ impl HandlerInner {
         diags.iter().for_each(|diag| self.emit_diagnostic(diag));
     }
 
+    // FIXME(eddyb) this should ideally take `diagnostic` by value.
     fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
-        if diagnostic.cancelled() {
-            return;
+        if diagnostic.level == Level::DelayedBug {
+            // FIXME(eddyb) this should check for `has_errors` and stop pushing
+            // once *any* errors were emitted (and truncate `delayed_span_bugs`
+            // when an error is first emitted, also), but maybe there's a case
+            // in which that's not sound? otherwise this is really inefficient.
+            self.delayed_span_bugs.push(diagnostic.clone());
+
+            if !self.flags.report_delayed_bugs {
+                return;
+            }
         }
 
         if diagnostic.has_future_breakage() {
@@ -1119,14 +1115,16 @@ impl HandlerInner {
             // FIXME: don't abort here if report_delayed_bugs is off
             self.span_bug(sp, msg);
         }
-        let mut diagnostic = Diagnostic::new(Level::Bug, msg);
+        let mut diagnostic = Diagnostic::new(Level::DelayedBug, msg);
         diagnostic.set_span(sp.into());
         diagnostic.note(&format!("delayed at {}", std::panic::Location::caller()));
-        self.delay_as_bug(diagnostic)
+        self.emit_diagnostic(&diagnostic)
     }
 
+    // FIXME(eddyb) note the comment inside `impl Drop for HandlerInner`, that's
+    // where the explanation of what "good path" is (also, it should be renamed).
     fn delay_good_path_bug(&mut self, msg: &str) {
-        let diagnostic = Diagnostic::new(Level::Bug, msg);
+        let diagnostic = Diagnostic::new(Level::DelayedBug, msg);
         if self.flags.report_delayed_bugs {
             self.emit_diagnostic(&diagnostic);
         }
@@ -1160,20 +1158,34 @@ impl HandlerInner {
         panic::panic_any(ExplicitBug);
     }
 
-    fn delay_as_bug(&mut self, diagnostic: Diagnostic) {
-        if self.flags.report_delayed_bugs {
-            self.emit_diagnostic(&diagnostic);
-        }
-        self.delayed_span_bugs.push(diagnostic);
-    }
+    fn flush_delayed(&mut self, bugs: impl IntoIterator<Item = Diagnostic>, explanation: &str) {
+        let mut no_bugs = true;
+        for mut bug in bugs {
+            if no_bugs {
+                // Put the overall explanation before the `DelayedBug`s, to
+                // frame them better (e.g. separate warnings from them).
+                self.emit_diagnostic(&Diagnostic::new(Bug, explanation));
+                no_bugs = false;
+            }
+
+            // "Undelay" the `DelayedBug`s (into plain `Bug`s).
+            if bug.level != Level::DelayedBug {
+                // NOTE(eddyb) not panicking here because we're already producing
+                // an ICE, and the more information the merrier.
+                bug.note(&format!(
+                    "`flushed_delayed` got diagnostic with level {:?}, \
+                     instead of the expected `DelayedBug`",
+                    bug.level,
+                ));
+            }
+            bug.level = Level::Bug;
 
-    fn flush_delayed(&mut self, bugs: Vec<Diagnostic>, explanation: &str) {
-        let has_bugs = !bugs.is_empty();
-        for bug in bugs {
             self.emit_diagnostic(&bug);
         }
-        if has_bugs {
-            panic!("{}", explanation);
+
+        // Panic with `ExplicitBug` to avoid "unexpected panic" messages.
+        if !no_bugs {
+            panic::panic_any(ExplicitBug);
         }
     }
 
@@ -1224,9 +1236,10 @@ impl DelayedDiagnostic {
     }
 }
 
-#[derive(Copy, PartialEq, Clone, Hash, Debug, Encodable, Decodable)]
+#[derive(Copy, PartialEq, Eq, Clone, Hash, Debug, Encodable, Decodable)]
 pub enum Level {
     Bug,
+    DelayedBug,
     Fatal,
     Error {
         /// If this error comes from a lint, don't abort compilation even when abort_if_errors() is called.
@@ -1235,7 +1248,6 @@ pub enum Level {
     Warning,
     Note,
     Help,
-    Cancelled,
     FailureNote,
     Allow,
 }
@@ -1250,7 +1262,7 @@ impl Level {
     fn color(self) -> ColorSpec {
         let mut spec = ColorSpec::new();
         match self {
-            Bug | Fatal | Error { .. } => {
+            Bug | DelayedBug | Fatal | Error { .. } => {
                 spec.set_fg(Some(Color::Red)).set_intense(true);
             }
             Warning => {
@@ -1263,20 +1275,19 @@ impl Level {
                 spec.set_fg(Some(Color::Cyan)).set_intense(true);
             }
             FailureNote => {}
-            Allow | Cancelled => unreachable!(),
+            Allow => unreachable!(),
         }
         spec
     }
 
     pub fn to_str(self) -> &'static str {
         match self {
-            Bug => "error: internal compiler error",
+            Bug | DelayedBug => "error: internal compiler error",
             Fatal | Error { .. } => "error",
             Warning => "warning",
             Note => "note",
             Help => "help",
             FailureNote => "failure-note",
-            Cancelled => panic!("Shouldn't call on cancelled error"),
             Allow => panic!("Shouldn't call on allowed error"),
         }
     }
@@ -1286,9 +1297,10 @@ impl Level {
     }
 }
 
+// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
 pub fn add_elided_lifetime_in_path_suggestion(
     source_map: &SourceMap,
-    db: &mut DiagnosticBuilder<'_>,
+    diag: &mut Diagnostic,
     n: usize,
     path_span: Span,
     incl_angl_brckt: bool,
@@ -1320,7 +1332,7 @@ pub fn add_elided_lifetime_in_path_suggestion(
             (insertion_span, anon_lts)
         }
     };
-    db.span_suggestion(
+    diag.span_suggestion(
         replace_span,
         &format!("indicate the anonymous lifetime{}", pluralize!(n)),
         suggestion,
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index 2bdf3b39126..4e951ad9d4b 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -10,7 +10,7 @@ use rustc_ast::{self as ast, AstLike, Attribute, Item, NodeId, PatKind};
 use rustc_attr::{self as attr, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{self, Lrc};
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported, PResult};
 use rustc_lint_defs::builtin::PROC_MACRO_BACK_COMPAT;
 use rustc_lint_defs::BuiltinLintDiagnostics;
 use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
@@ -1072,7 +1072,11 @@ impl<'a> ExtCtxt<'a> {
         self.current_expansion.id.expansion_cause()
     }
 
-    pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
+    pub fn struct_span_err<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        msg: &str,
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg)
     }
 
@@ -1130,11 +1134,7 @@ impl<'a> ExtCtxt<'a> {
     /// This unifies the logic used for resolving `include_X!`.
     ///
     /// FIXME: move this to `rustc_builtin_macros` and make it private.
-    pub fn resolve_path(
-        &self,
-        path: impl Into<PathBuf>,
-        span: Span,
-    ) -> Result<PathBuf, DiagnosticBuilder<'a>> {
+    pub fn resolve_path(&self, path: impl Into<PathBuf>, span: Span) -> PResult<'a, PathBuf> {
         let path = path.into();
 
         // Relative paths are resolved relative to the file in which they are found
@@ -1174,7 +1174,7 @@ pub fn expr_to_spanned_string<'a>(
     cx: &'a mut ExtCtxt<'_>,
     expr: P<ast::Expr>,
     err_msg: &str,
-) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a>, bool)>> {
+) -> Result<(Symbol, ast::StrStyle, Span), Option<(DiagnosticBuilder<'a, ErrorReported>, bool)>> {
     // Perform eager expansion on the expression.
     // We want to be able to handle e.g., `concat!("foo", "bar")`.
     let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
@@ -1233,7 +1233,9 @@ pub fn check_zero_tts(cx: &ExtCtxt<'_>, sp: Span, tts: TokenStream, name: &str)
 pub fn parse_expr(p: &mut parser::Parser<'_>) -> Option<P<ast::Expr>> {
     match p.parse_expr() {
         Ok(e) => return Some(e),
-        Err(mut err) => err.emit(),
+        Err(mut err) => {
+            err.emit();
+        }
     }
     while p.token != token::Eof {
         p.bump();
diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs
index f71fb58cf6b..4af7d2b7ec6 100644
--- a/compiler/rustc_expand/src/mbe/macro_rules.rs
+++ b/compiler/rustc_expand/src/mbe/macro_rules.rs
@@ -16,7 +16,7 @@ use rustc_ast_pretty::pprust;
 use rustc_attr::{self as attr, TransparencyError};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
 use rustc_feature::Features;
 use rustc_lint_defs::builtin::{
     RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
@@ -49,11 +49,7 @@ crate struct ParserAnyMacro<'a> {
     is_local: bool,
 }
 
-crate fn annotate_err_with_kind(
-    err: &mut DiagnosticBuilder<'_>,
-    kind: AstFragmentKind,
-    span: Span,
-) {
+crate fn annotate_err_with_kind(err: &mut Diagnostic, kind: AstFragmentKind, span: Span) {
     match kind {
         AstFragmentKind::Ty => {
             err.span_label(span, "this macro call doesn't expand to a type");
@@ -66,7 +62,7 @@ crate fn annotate_err_with_kind(
 }
 
 fn emit_frag_parse_err(
-    mut e: DiagnosticBuilder<'_>,
+    mut e: DiagnosticBuilder<'_, rustc_errors::ErrorReported>,
     parser: &Parser<'_>,
     orig_parser: &mut Parser<'_>,
     site_span: Span,
@@ -99,7 +95,7 @@ fn emit_frag_parse_err(
     match kind {
         // Try a statement if an expression is wanted but failed and suggest adding `;` to call.
         AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
-            Err(mut err) => err.cancel(),
+            Err(err) => err.cancel(),
             Ok(_) => {
                 e.note(
                     "the macro call doesn't expand to an expression, but it can expand to a statement",
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index aa54bdbd3a7..1ce3766579b 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -1,7 +1,7 @@
 use crate::base::ModuleData;
 use rustc_ast::ptr::P;
 use rustc_ast::{token, Attribute, Inline, Item};
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
 use rustc_parse::new_parser_from_file;
 use rustc_parse::validate_attr;
 use rustc_session::parse::ParseSess;
@@ -39,7 +39,7 @@ pub enum ModError<'a> {
     ModInBlock(Option<Ident>),
     FileNotFound(Ident, PathBuf, PathBuf),
     MultipleCandidates(Ident, PathBuf, PathBuf),
-    ParserError(DiagnosticBuilder<'a>),
+    ParserError(DiagnosticBuilder<'a, ErrorReported>),
 }
 
 crate fn parse_external_mod(
@@ -242,7 +242,7 @@ pub fn default_submod_path<'a>(
 }
 
 impl ModError<'_> {
-    fn report(self, sess: &Session, span: Span) {
+    fn report(self, sess: &Session, span: Span) -> ErrorReported {
         let diag = &sess.parse_sess.span_diagnostic;
         match self {
             ModError::CircularInclusion(file_paths) => {
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 99a945b1c91..869cada400f 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -446,7 +446,9 @@ impl server::TokenStream for Rustc<'_, '_> {
             }
             expr
         };
-        let expr = expr.map_err(|mut err| err.emit())?;
+        let expr = expr.map_err(|mut err| {
+            err.emit();
+        })?;
 
         // Perform eager expansion on the expression.
         let expr = self
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index d48c8e81f54..72c02932945 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -15,6 +15,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sorted_map::SortedMap;
 use rustc_index::vec::IndexVec;
 use rustc_macros::HashStable_Generic;
+use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{def_id::LocalDefId, BytePos, MultiSpan, Span, DUMMY_SP};
@@ -2803,7 +2804,7 @@ pub enum ItemKind<'hir> {
     /// A function declaration.
     Fn(FnSig<'hir>, Generics<'hir>, BodyId),
     /// A MBE macro definition (`macro_rules!` or `macro`).
-    Macro(ast::MacroDef),
+    Macro(ast::MacroDef, MacroKind),
     /// A module.
     Mod(Mod<'hir>),
     /// An external module, e.g. `extern { .. }`.
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index c55f2a7b039..1b40f3d390e 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -575,7 +575,7 @@ pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
             item.span,
             item.hir_id(),
         ),
-        ItemKind::Macro(_) => {
+        ItemKind::Macro(..) => {
             visitor.visit_id(item.hir_id());
         }
         ItemKind::Mod(ref module) => {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 8e45b636f47..b3042c61002 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -570,7 +570,7 @@ impl<'a> State<'a> {
                 self.end(); // need to close a box
                 self.ann.nested(self, Nested::Body(body));
             }
-            hir::ItemKind::Macro(ref macro_def) => {
+            hir::ItemKind::Macro(ref macro_def, _) => {
                 self.print_mac_def(macro_def, &item.ident, item.span, |state| {
                     state.print_visibility(&item.vis)
                 });
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 91215cbb17c..317481a037d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -58,7 +58,7 @@ use crate::traits::{
 };
 
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err};
+use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorReported};
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
@@ -85,7 +85,7 @@ pub mod nice_region_error;
 
 pub(super) fn note_and_explain_region<'tcx>(
     tcx: TyCtxt<'tcx>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     prefix: &str,
     region: ty::Region<'tcx>,
     suffix: &str,
@@ -118,7 +118,7 @@ pub(super) fn note_and_explain_region<'tcx>(
 
 fn explain_free_region<'tcx>(
     tcx: TyCtxt<'tcx>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     prefix: &str,
     region: ty::Region<'tcx>,
     suffix: &str,
@@ -194,7 +194,7 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>(
 }
 
 fn emit_msg_span(
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     prefix: &str,
     description: String,
     span: Option<Span>,
@@ -210,7 +210,7 @@ fn emit_msg_span(
 }
 
 fn label_msg_span(
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     prefix: &str,
     description: String,
     span: Option<Span>,
@@ -230,7 +230,7 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
     span: Span,
     hidden_ty: Ty<'tcx>,
     hidden_region: ty::Region<'tcx>,
-) -> DiagnosticBuilder<'tcx> {
+) -> DiagnosticBuilder<'tcx, ErrorReported> {
     let mut err = struct_span_err!(
         tcx.sess,
         span,
@@ -471,11 +471,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     }
 
     /// Adds a note if the types come from similarly named crates
-    fn check_and_note_conflicting_crates(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        terr: &TypeError<'tcx>,
-    ) {
+    fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: &TypeError<'tcx>) {
         use hir::def_id::CrateNum;
         use rustc_hir::definitions::DisambiguatedDefPathData;
         use ty::print::Printer;
@@ -557,7 +553,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
             }
         }
 
-        let report_path_match = |err: &mut DiagnosticBuilder<'_>, did1: DefId, did2: DefId| {
+        let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| {
             // Only external crates, if either is from a local
             // module we could have false positives
             if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate {
@@ -598,7 +594,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     fn note_error_origin(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         cause: &ObligationCause<'tcx>,
         exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>,
         terr: &TypeError<'tcx>,
@@ -792,7 +788,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     fn suggest_boxing_for_return_impl_trait(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         return_sp: Span,
         arm_spans: impl Iterator<Item = Span>,
     ) {
@@ -1436,7 +1432,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     /// E0271, like `src/test/ui/issues/issue-39970.stderr`.
     pub fn note_type_err(
         &self,
-        diag: &mut DiagnosticBuilder<'tcx>,
+        diag: &mut Diagnostic,
         cause: &ObligationCause<'tcx>,
         secondary_span: Option<(Span, String)>,
         mut values: Option<ValuePairs<'tcx>>,
@@ -1483,14 +1479,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                 types_visitor
             }
 
-            fn report(&self, err: &mut DiagnosticBuilder<'_>) {
+            fn report(&self, err: &mut Diagnostic) {
                 self.add_labels_for_types(err, "expected", &self.expected);
                 self.add_labels_for_types(err, "found", &self.found);
             }
 
             fn add_labels_for_types(
                 &self,
-                err: &mut DiagnosticBuilder<'_>,
+                err: &mut Diagnostic,
                 target: &str,
                 types: &FxHashMap<TyCategory, FxHashSet<Span>>,
             ) {
@@ -1601,7 +1597,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
                     Some((expected, found)) => Some((expected, found)),
                     None => {
                         // Derived error. Cancel the emitter.
-                        diag.cancel();
+                        // NOTE(eddyb) this was `.cancel()`, but `diag`
+                        // is borrowed, so we can't fully defuse it.
+                        diag.downgrade_to_delayed_bug();
                         return;
                     }
                 };
@@ -1817,7 +1815,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         exp_span: Span,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut DiagnosticBuilder<'tcx>,
+        diag: &mut Diagnostic,
     ) {
         debug!(
             "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
@@ -1905,7 +1903,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         cause: &ObligationCause<'tcx>,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut DiagnosticBuilder<'tcx>,
+        diag: &mut Diagnostic,
     ) {
         debug!(
             "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
@@ -1954,7 +1952,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         span: Span,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
-        diag: &mut DiagnosticBuilder<'tcx>,
+        diag: &mut Diagnostic,
     ) {
         if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
             (exp_found.expected.kind(), exp_found.found.kind())
@@ -2015,7 +2013,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         trace: TypeTrace<'tcx>,
         terr: &TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         use crate::traits::ObligationCauseCode::MatchExpressionArm;
 
         debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr);
@@ -2107,7 +2105,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
 
     fn emit_tuple_wrap_err(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         span: Span,
         found: Ty<'tcx>,
         expected_fields: &List<Ty<'tcx>>,
@@ -2223,7 +2221,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: Option<SubregionOrigin<'tcx>>,
         bound_kind: GenericKind<'tcx>,
         sub: Region<'tcx>,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let hir = self.tcx.hir();
         // Attempt to obtain the span of the parameter so we can
         // suggest adding an explicit lifetime bound to it.
@@ -2339,7 +2337,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
 
         fn binding_suggestion<'tcx, S: fmt::Display>(
-            err: &mut DiagnosticBuilder<'tcx>,
+            err: &mut Diagnostic,
             type_param_span: Option<(Span, bool, bool)>,
             bound_kind: GenericKind<'tcx>,
             sub: S,
@@ -2373,7 +2371,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         }
 
         let new_binding_suggestion =
-            |err: &mut DiagnosticBuilder<'tcx>,
+            |err: &mut Diagnostic,
              type_param_span: Option<(Span, bool, bool)>,
              bound_kind: GenericKind<'tcx>| {
                 let msg = "consider introducing an explicit lifetime bound";
@@ -2649,7 +2647,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
     fn report_inference_failure(
         &self,
         var_origin: RegionVariableOrigin,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let br_string = |br: ty::BoundRegionKind| {
             let mut s = match br {
                 ty::BrNamed(_, name) => name.to_string(),
diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
index ff734c99f3e..067f19bbe94 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs
@@ -1,6 +1,8 @@
 use crate::infer::type_variable::TypeVariableOriginKind;
 use crate::infer::{InferCtxt, Symbol};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Namespace};
 use rustc_hir::def_id::DefId;
@@ -195,7 +197,7 @@ impl UseDiagnostic<'_> {
         }
     }
 
-    fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn attach_note(&self, err: &mut Diagnostic) {
         match *self {
             Self::TryConversion { pre_ty, post_ty, .. } => {
                 let intro = "`?` implicitly converts the error value";
@@ -224,7 +226,7 @@ impl UseDiagnostic<'_> {
 
 /// Suggest giving an appropriate return type to a closure expression.
 fn closure_return_type_suggestion(
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     output: &FnRetTy<'_>,
     body: &Body<'_>,
     ret: &str,
@@ -488,7 +490,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         arg: GenericArg<'tcx>,
         impl_candidates: Vec<ty::TraitRef<'tcx>>,
         error_code: TypeAnnotationNeeded,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let arg = self.resolve_vars_if_possible(arg);
         let arg_data = self.extract_inference_diagnostics_data(arg, None);
 
@@ -868,7 +870,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         &self,
         segment: &hir::PathSegment<'_>,
         e: &Expr<'_>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     ) {
         if let (Some(typeck_results), None) = (self.in_progress_typeck_results, &segment.args) {
             let borrow = typeck_results.borrow();
@@ -913,7 +915,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         kind: hir::GeneratorKind,
         span: Span,
         ty: Ty<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let ty = self.resolve_vars_if_possible(ty);
         let data = self.extract_inference_diagnostics_data(ty.into(), None);
 
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
index 4eec492b3ae..bbbb0f79acc 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs
@@ -7,7 +7,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
 use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::SubregionOrigin;
 
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::{GenericParamKind, Ty};
 use rustc_middle::ty::Region;
@@ -156,7 +156,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
         sub: Region<'tcx>,
         ty_sup: &Ty<'_>,
         ty_sub: &Ty<'_>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     ) {
         if let (
             hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index f44e6e04346..a8b878ae344 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -46,7 +46,7 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
         self.infcx.tcx
     }
 
-    pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx>> {
+    pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
         // the nice region errors are required when running under the MIR borrow checker.
         self.try_report_named_anon_conflict().or_else(|| self.try_report_placeholder_conflict())
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 17ff5d45c89..825aadaad9a 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -2,13 +2,15 @@
 //! where one region is named and the other is anonymous.
 use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type;
 use crate::infer::error_reporting::nice_region_error::NiceRegionError;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_middle::ty;
 
 impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// When given a `ConcreteFailure` for a function with parameters containing a named region and
     /// an anonymous region, emit an descriptive diagnostic error.
-    pub(super) fn try_report_named_anon_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
+    pub(super) fn try_report_named_anon_conflict(
+        &self,
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         let (span, sub, sup) = self.regions()?;
 
         debug!(
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index bf9c2246dd8..5ce5d44e51d 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -4,7 +4,7 @@ use crate::infer::ValuePairs;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCause, ObligationCauseCode};
 use rustc_data_structures::intern::Interned;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::error::ExpectedFound;
@@ -17,7 +17,9 @@ use std::fmt::{self, Write};
 impl<'tcx> NiceRegionError<'_, 'tcx> {
     /// When given a `ConcreteFailure` for a function with arguments containing a named region and
     /// an anonymous region, emit a descriptive diagnostic error.
-    pub(super) fn try_report_placeholder_conflict(&self) -> Option<DiagnosticBuilder<'tcx>> {
+    pub(super) fn try_report_placeholder_conflict(
+        &self,
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         match &self.error {
             ///////////////////////////////////////////////////////////////////////////
             // NB. The ordering of cases in this match is very
@@ -153,7 +155,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
         sub_placeholder: Option<Region<'tcx>>,
         sup_placeholder: Option<Region<'tcx>>,
         value_pairs: &ValuePairs<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         let (expected_substs, found_substs, trait_def_id) = match value_pairs {
             ValuePairs::TraitRefs(ExpectedFound { expected, found })
                 if expected.def_id == found.def_id =>
@@ -201,7 +203,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
         trait_def_id: DefId,
         expected_substs: SubstsRef<'tcx>,
         actual_substs: SubstsRef<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let span = cause.span(self.tcx());
         let msg = format!(
             "implementation of `{}` is not general enough",
@@ -306,7 +308,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
     /// due to the number of combinations we have to deal with.
     fn explain_actual_impl_that_was_found(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         sub_placeholder: Option<Region<'tcx>>,
         sup_placeholder: Option<Region<'tcx>>,
         has_sub: Option<usize>,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 8601180842c..210743d7cef 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -5,7 +5,7 @@ use crate::infer::lexical_region_resolve::RegionResolutionError;
 use crate::infer::{SubregionOrigin, TypeTrace};
 use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
 use rustc_data_structures::stable_set::FxHashSet;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind};
@@ -286,7 +286,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
 pub fn suggest_new_region_bound(
     tcx: TyCtxt<'_>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     fn_returns: Vec<&rustc_hir::Ty<'_>>,
     lifetime_name: String,
     arg: Option<String>,
@@ -483,7 +483,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
     /// `'static` obligation. Suggest relaxing that implicit bound.
     fn find_impl_on_dyn_trait(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         ty: Ty<'_>,
         ctxt: &UnifyReceiverContext<'tcx>,
     ) -> bool {
@@ -514,7 +514,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
 
     fn suggest_constrain_dyn_trait_in_impl(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         found_dids: &FxHashSet<DefId>,
         ident: Ident,
         self_ty: &hir::Ty<'_>,
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 8671ecba6e9..5dc8c894608 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -1,16 +1,12 @@
 use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt};
 use crate::infer::{self, InferCtxt, SubregionOrigin};
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_middle::traits::ObligationCauseCode;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::{self, Region};
 
 impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
-    pub(super) fn note_region_origin(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        origin: &SubregionOrigin<'tcx>,
-    ) {
+    pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
         let mut label_or_note = |span, msg| {
             let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count();
             let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count();
@@ -113,7 +109,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         origin: SubregionOrigin<'tcx>,
         sub: Region<'tcx>,
         sup: Region<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         match origin {
             infer::Subtype(box trace) => {
                 let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
@@ -405,7 +401,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         placeholder_origin: SubregionOrigin<'tcx>,
         sub: Region<'tcx>,
         sup: Region<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         // I can't think how to do better than this right now. -nikomatsakis
         debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
         match placeholder_origin {
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 57ac98ca897..95608f413d5 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::undo_log::Rollback;
 use rustc_data_structures::unify as ut;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::{DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues};
@@ -1475,19 +1475,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         sp: Span,
         mk_diag: M,
         actual_ty: Ty<'tcx>,
-    ) -> DiagnosticBuilder<'tcx>
+    ) -> DiagnosticBuilder<'tcx, ErrorReported>
     where
-        M: FnOnce(String) -> DiagnosticBuilder<'tcx>,
+        M: FnOnce(String) -> DiagnosticBuilder<'tcx, ErrorReported>,
     {
         let actual_ty = self.resolve_vars_if_possible(actual_ty);
         debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty);
 
+        let mut err = mk_diag(self.ty_to_string(actual_ty));
+
         // Don't report an error if actual type is `Error`.
         if actual_ty.references_error() {
-            return self.tcx.sess.diagnostic().struct_dummy();
+            err.downgrade_to_delayed_bug();
         }
 
-        mk_diag(self.ty_to_string(actual_ty))
+        err
     }
 
     pub fn report_mismatched_types(
@@ -1496,7 +1498,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         err: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let trace = TypeTrace::types(cause, true, expected, actual);
         self.report_and_explain_type_error(trace, &err)
     }
@@ -1507,7 +1509,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         expected: ty::Const<'tcx>,
         actual: ty::Const<'tcx>,
         err: TypeError<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let trace = TypeTrace::consts(cause, true, expected, actual);
         self.report_and_explain_type_error(trace, &err)
     }
diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
index 1a5ffd93701..35430849290 100644
--- a/compiler/rustc_infer/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs
@@ -2,7 +2,7 @@ use super::ObjectSafetyViolation;
 
 use crate::infer::InferCtxt;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::TyCtxt;
@@ -17,7 +17,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
         impl_item_def_id: DefId,
         trait_item_def_id: DefId,
         requirement: &dyn fmt::Display,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let msg = "impl has stricter requirements than trait";
         let sp = self.tcx.sess.source_map().guess_head_span(error_span);
 
@@ -40,7 +40,7 @@ pub fn report_object_safety_error<'tcx>(
     span: Span,
     trait_def_id: DefId,
     violations: &[ObjectSafetyViolation],
-) -> DiagnosticBuilder<'tcx> {
+) -> DiagnosticBuilder<'tcx, ErrorReported> {
     let trait_str = tcx.def_path_str(trait_def_id);
     let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node {
         hir::Node::Item(item) => Some(item.ident.span),
diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs
index 609fc4b78c0..20835262324 100644
--- a/compiler/rustc_interface/src/interface.rs
+++ b/compiler/rustc_interface/src/interface.rs
@@ -102,7 +102,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
                 }
 
                 match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
-                    Ok(mut parser) => match &mut parser.parse_meta_item() {
+                    Ok(mut parser) => match parser.parse_meta_item() {
                         Ok(meta_item) if parser.token == token::Eof => {
                             if meta_item.path.segments.len() != 1 {
                                 error!("argument key must be an identifier");
@@ -121,7 +121,7 @@ pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String
                         Ok(..) => {}
                         Err(err) => err.cancel(),
                     },
-                    Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()),
+                    Err(errs) => drop(errs),
                 }
 
                 // If the user tried to use a key="value" flag, but is missing the quotes, provide
@@ -165,15 +165,16 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
             }
 
             match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
-                Ok(mut parser) => match &mut parser.parse_meta_item() {
+                Ok(mut parser) => match parser.parse_meta_item() {
                     Ok(meta_item) if parser.token == token::Eof => {
                         if let Some(args) = meta_item.meta_item_list() {
                             if meta_item.has_name(sym::names) {
-                                cfg.names_checked = true;
+                                let names_valid =
+                                    cfg.names_valid.get_or_insert_with(|| FxHashSet::default());
                                 for arg in args {
                                     if arg.is_word() && arg.ident().is_some() {
                                         let ident = arg.ident().expect("multi-segment cfg key");
-                                        cfg.names_valid.insert(ident.name.to_string());
+                                        names_valid.insert(ident.name.to_string());
                                     } else {
                                         error!("`names()` arguments must be simple identifers");
                                     }
@@ -183,13 +184,16 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                                 if let Some((name, values)) = args.split_first() {
                                     if name.is_word() && name.ident().is_some() {
                                         let ident = name.ident().expect("multi-segment cfg key");
-                                        cfg.values_checked.insert(ident.to_string());
+                                        let ident_values = cfg
+                                            .values_valid
+                                            .entry(ident.name.to_string())
+                                            .or_insert_with(|| FxHashSet::default());
+
                                         for val in values {
                                             if let Some(LitKind::Str(s, _)) =
                                                 val.literal().map(|lit| &lit.kind)
                                             {
-                                                cfg.values_valid
-                                                    .insert((ident.to_string(), s.to_string()));
+                                                ident_values.insert(s.to_string());
                                             } else {
                                                 error!(
                                                     "`values()` arguments must be string literals"
@@ -210,7 +214,7 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
                     Ok(..) => {}
                     Err(err) => err.cancel(),
                 },
-                Err(errs) => errs.into_iter().for_each(|mut err| err.cancel()),
+                Err(errs) => drop(errs),
             }
 
             error!(
@@ -219,7 +223,9 @@ pub fn parse_check_cfg(specs: Vec<String>) -> CheckCfg {
             );
         }
 
-        cfg.names_valid.extend(cfg.values_checked.iter().cloned());
+        if let Some(names_valid) = &mut cfg.names_valid {
+            names_valid.extend(cfg.values_valid.keys().cloned());
+        }
         cfg
     })
 }
diff --git a/compiler/rustc_lexer/src/unescape.rs b/compiler/rustc_lexer/src/unescape.rs
index d789237e692..97f9588ae1e 100644
--- a/compiler/rustc_lexer/src/unescape.rs
+++ b/compiler/rustc_lexer/src/unescape.rs
@@ -159,26 +159,8 @@ impl Mode {
     }
 }
 
-fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
-    if first_char != '\\' {
-        // Previous character was not a slash, and we don't expect it to be
-        // an escape-only character.
-        return match first_char {
-            '\t' | '\n' => Err(EscapeError::EscapeOnlyChar),
-            '\r' => Err(EscapeError::BareCarriageReturn),
-            '\'' if mode.in_single_quotes() => Err(EscapeError::EscapeOnlyChar),
-            '"' if mode.in_double_quotes() => Err(EscapeError::EscapeOnlyChar),
-            _ => {
-                if mode.is_bytes() && !first_char.is_ascii() {
-                    // Byte literal can't be a non-ascii character.
-                    return Err(EscapeError::NonAsciiCharInByte);
-                }
-                Ok(first_char)
-            }
-        };
-    }
-
-    // Previous character is '\\', try to unescape it.
+fn scan_escape(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
+    // Previous character was '\\', unescape what follows.
 
     let second_char = chars.next().ok_or(EscapeError::LoneSlash)?;
 
@@ -270,9 +252,24 @@ fn scan_escape(first_char: char, chars: &mut Chars<'_>, mode: Mode) -> Result<ch
     Ok(res)
 }
 
+#[inline]
+fn ascii_check(first_char: char, mode: Mode) -> Result<char, EscapeError> {
+    if mode.is_bytes() && !first_char.is_ascii() {
+        // Byte literal can't be a non-ascii character.
+        Err(EscapeError::NonAsciiCharInByte)
+    } else {
+        Ok(first_char)
+    }
+}
+
 fn unescape_char_or_byte(chars: &mut Chars<'_>, mode: Mode) -> Result<char, EscapeError> {
     let first_char = chars.next().ok_or(EscapeError::ZeroChars)?;
-    let res = scan_escape(first_char, chars, mode)?;
+    let res = match first_char {
+        '\\' => scan_escape(chars, mode),
+        '\n' | '\t' | '\'' => Err(EscapeError::EscapeOnlyChar),
+        '\r' => Err(EscapeError::BareCarriageReturn),
+        _ => ascii_check(first_char, mode),
+    }?;
     if chars.next().is_some() {
         return Err(EscapeError::MoreThanOneChar);
     }
@@ -303,12 +300,14 @@ where
                         skip_ascii_whitespace(&mut chars, start, callback);
                         continue;
                     }
-                    _ => scan_escape(first_char, &mut chars, mode),
+                    _ => scan_escape(&mut chars, mode),
                 }
             }
             '\n' => Ok('\n'),
             '\t' => Ok('\t'),
-            _ => scan_escape(first_char, &mut chars, mode),
+            '"' => Err(EscapeError::EscapeOnlyChar),
+            '\r' => Err(EscapeError::BareCarriageReturn),
+            _ => ascii_check(first_char, mode),
         };
         let end = initial_len - chars.as_str().len();
         callback(start..end, unescaped_char);
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs
index 30b5f9b34d0..46b90baa585 100644
--- a/compiler/rustc_lint/src/builtin.rs
+++ b/compiler/rustc_lint/src/builtin.rs
@@ -31,7 +31,7 @@ use rustc_ast::{self as ast, *};
 use rustc_ast_pretty::pprust::{self, expr_to_string};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticStyledString};
 use rustc_feature::{deprecated_attributes, AttributeGate, BuiltinAttribute, GateIssue, Stability};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
@@ -51,7 +51,7 @@ use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::{BytePos, InnerSpan, MultiSpan, Span};
 use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::traits::misc::can_type_implement_copy;
+use rustc_trait_selection::traits::{self, misc::can_type_implement_copy};
 
 use crate::nonstandard_style::{method_context, MethodLateContext};
 
@@ -764,7 +764,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
         if ty.is_copy_modulo_regions(cx.tcx.at(item.span), param_env) {
             return;
         }
-        if can_type_implement_copy(cx.tcx, param_env, ty).is_ok() {
+        if can_type_implement_copy(
+            cx.tcx,
+            param_env,
+            ty,
+            traits::ObligationCause::misc(item.span, item.hir_id()),
+        )
+        .is_ok()
+        {
             cx.struct_span_lint(MISSING_COPY_IMPLEMENTATIONS, item.span, |lint| {
                 lint.build(
                     "type could implement `Copy`; consider adding `impl \
@@ -1469,17 +1476,17 @@ impl TypeAliasBounds {
         }
     }
 
-    fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_changing_assoc_types(ty: &hir::Ty<'_>, err: &mut Diagnostic) {
         // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
         // bound.  Let's see if this type does that.
 
         // We use a HIR visitor to walk the type.
         use rustc_hir::intravisit::{self, Visitor};
-        struct WalkAssocTypes<'a, 'db> {
-            err: &'a mut DiagnosticBuilder<'db>,
+        struct WalkAssocTypes<'a> {
+            err: &'a mut Diagnostic,
         }
-        impl<'a, 'db, 'v> Visitor<'v> for WalkAssocTypes<'a, 'db> {
-            fn visit_qpath(&mut self, qpath: &'v hir::QPath<'v>, id: hir::HirId, span: Span) {
+        impl Visitor<'_> for WalkAssocTypes<'_> {
+            fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, span: Span) {
                 if TypeAliasBounds::is_type_variable_assoc(qpath) {
                     self.err.span_help(
                         span,
diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs
index d8f55292ccd..ac0f4bd8a4d 100644
--- a/compiler/rustc_lint/src/context.rs
+++ b/compiler/rustc_lint/src/context.rs
@@ -319,7 +319,7 @@ impl LintStore {
     ) {
         let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
         if lint_name_only == crate::WARNINGS.name_lower() && level == Level::ForceWarn {
-            return struct_span_err!(
+            struct_span_err!(
                 sess,
                 DUMMY_SP,
                 E0602,
@@ -327,6 +327,7 @@ impl LintStore {
                 crate::WARNINGS.name_lower()
             )
             .emit();
+            return;
         }
         let db = match self.check_lint_name(lint_name_only, tool_name, registered_tools) {
             CheckLintNameResult::Ok(_) => None,
@@ -339,7 +340,7 @@ impl LintStore {
                     err.help(&format!("did you mean: `{}`", suggestion));
                 }
 
-                Some(err)
+                Some(err.forget_guarantee())
             }
             CheckLintNameResult::Tool(result) => match result {
                 Err((Some(_), new_name)) => Some(sess.struct_warn(&format!(
@@ -350,13 +351,16 @@ impl LintStore {
                 ))),
                 _ => None,
             },
-            CheckLintNameResult::NoTool => Some(struct_span_err!(
-                sess,
-                DUMMY_SP,
-                E0602,
-                "unknown lint tool: `{}`",
-                tool_name.unwrap()
-            )),
+            CheckLintNameResult::NoTool => Some(
+                struct_span_err!(
+                    sess,
+                    DUMMY_SP,
+                    E0602,
+                    "unknown lint tool: `{}`",
+                    tool_name.unwrap()
+                )
+                .forget_guarantee(),
+            ),
         };
 
         if let Some(mut db) = db {
@@ -765,7 +769,40 @@ pub trait LintContext: Sized {
                 BuiltinLintDiagnostics::NamedAsmLabel(help) => {
                     db.help(&help);
                     db.note("see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information");
-                }
+                },
+                BuiltinLintDiagnostics::UnexpectedCfg(span, name, value) => {
+                    let possibilities: Vec<Symbol> = if value.is_some() {
+                        let Some(values) = &sess.parse_sess.check_config.values_valid.get(&name) else {
+                            bug!("it shouldn't be possible to have a diagnostic on a value whose name is not in values");
+                        };
+                        values.iter().map(|&s| s).collect()
+                    } else {
+                        let Some(names_valid) = &sess.parse_sess.check_config.names_valid else {
+                            bug!("it shouldn't be possible to have a diagnostic on a name if name checking is not enabled");
+                        };
+                        names_valid.iter().map(|s| *s).collect()
+                    };
+
+                    // Show the full list if all possible values for a given name, but don't do it
+                    // for names as the possibilities could be very long
+                    if value.is_some() {
+                        if !possibilities.is_empty() {
+                            let mut possibilities = possibilities.iter().map(Symbol::as_str).collect::<Vec<_>>();
+                            possibilities.sort();
+
+                            let possibilities = possibilities.join(", ");
+                            db.note(&format!("expected values for `{name}` are: {possibilities}"));
+                        } else {
+                            db.note(&format!("no expected value for `{name}`"));
+                        }
+                    }
+
+                    // Suggest the most probable if we found one
+                    if let Some(best_match) = find_best_match_for_name(&possibilities, value.unwrap_or(name), None) {
+                        let punctuation = if value.is_some() { "\"" } else { "" };
+                        db.span_suggestion(span, "did you mean", format!("{punctuation}{best_match}{punctuation}"), Applicability::MaybeIncorrect);
+                    }
+                },
             }
             // Rewrap `db`, and pass control to the user.
             decorate(LintDiagnosticBuilder::new(db));
diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index 35c7d885e1d..d7cdb08d817 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, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::{intravisit, HirId};
 use rustc_middle::hir::nested_filter;
@@ -150,29 +150,28 @@ impl<'s> LintLevelsBuilder<'s> {
                     fcw_warning, specs, old_src, id_name
                 );
 
-                let decorate_diag_builder = |mut diag_builder: DiagnosticBuilder<'_>| {
-                    diag_builder.span_label(src.span(), "overruled by previous forbid");
+                let decorate_diag = |diag: &mut Diagnostic| {
+                    diag.span_label(src.span(), "overruled by previous forbid");
                     match old_src {
                         LintLevelSource::Default => {
-                            diag_builder.note(&format!(
+                            diag.note(&format!(
                                 "`forbid` lint level is the default for {}",
                                 id.to_string()
                             ));
                         }
                         LintLevelSource::Node(_, forbid_source_span, reason) => {
-                            diag_builder.span_label(forbid_source_span, "`forbid` level set here");
+                            diag.span_label(forbid_source_span, "`forbid` level set here");
                             if let Some(rationale) = reason {
-                                diag_builder.note(rationale.as_str());
+                                diag.note(rationale.as_str());
                             }
                         }
                         LintLevelSource::CommandLine(_, _) => {
-                            diag_builder.note("`forbid` lint level was set on command line");
+                            diag.note("`forbid` lint level was set on command line");
                         }
                     }
-                    diag_builder.emit();
                 };
                 if !fcw_warning {
-                    let diag_builder = struct_span_err!(
+                    let mut diag_builder = struct_span_err!(
                         self.sess,
                         src.span(),
                         E0453,
@@ -180,18 +179,20 @@ impl<'s> LintLevelsBuilder<'s> {
                         level.as_str(),
                         src.name(),
                     );
-                    decorate_diag_builder(diag_builder);
+                    decorate_diag(&mut diag_builder);
+                    diag_builder.emit();
                 } else {
                     self.struct_lint(
                         FORBIDDEN_LINT_GROUPS,
                         Some(src.span().into()),
                         |diag_builder| {
-                            let diag_builder = diag_builder.build(&format!(
+                            let mut diag_builder = diag_builder.build(&format!(
                                 "{}({}) incompatible with previous forbid",
                                 level.as_str(),
                                 src.name(),
                             ));
-                            decorate_diag_builder(diag_builder);
+                            decorate_diag(&mut diag_builder);
+                            diag_builder.emit();
                         },
                     );
                 }
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index 69863b5ff82..7182022d252 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -31,6 +31,7 @@
 #![feature(box_patterns)]
 #![feature(crate_visibility_modifier)]
 #![feature(if_let_guard)]
+#![feature(iter_intersperse)]
 #![feature(iter_order_by)]
 #![feature(let_else)]
 #![feature(never_type)]
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 1f834b7212f..e9c62fc4006 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -310,6 +310,7 @@ pub enum BuiltinLintDiagnostics {
     BreakWithLabelAndLoop(Span),
     NamedAsmLabel(String),
     UnicodeTextFlow(Span, String),
+    UnexpectedCfg(Span, Symbol, Option<Symbol>),
 }
 
 /// Lints that are buffered up early on in the `Session` before the
diff --git a/compiler/rustc_macros/src/session_diagnostic.rs b/compiler/rustc_macros/src/session_diagnostic.rs
index 80dcf99da62..9a7784ca72f 100644
--- a/compiler/rustc_macros/src/session_diagnostic.rs
+++ b/compiler/rustc_macros/src/session_diagnostic.rs
@@ -232,7 +232,7 @@ impl<'a> SessionDiagnosticDerive<'a> {
                 fn into_diagnostic(
                     self,
                     #sess: &'__session_diagnostic_sess rustc_session::Session
-                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess> {
+                ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, rustc_errors::ErrorReported> {
                     #implementation
                 }
             }
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 0f10c269a04..dce1b35c6b8 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -243,7 +243,9 @@ impl Collector<'_> {
         if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
             let msg = "native frameworks are only available on macOS targets";
             match span {
-                Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
+                Some(span) => {
+                    struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit();
+                }
                 None => self.tcx.sess.err(msg),
             }
         }
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index aaa44a68dc0..e5e0cce198f 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -5,6 +5,7 @@ use crate::rmeta::table::{FixedSizeEncoding, Table};
 use crate::rmeta::*;
 
 use rustc_ast as ast;
+use rustc_ast::ptr::P;
 use rustc_attr as attr;
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashMap;
@@ -1076,6 +1077,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                         res,
                         vis: ty::Visibility::Public,
                         span: ident.span,
+                        macro_rules: false,
                     });
                 }
             }
@@ -1087,17 +1089,19 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             for child_index in children.decode((self, sess)) {
                 if let Some(ident) = self.opt_item_ident(child_index, sess) {
                     let kind = self.def_kind(child_index);
-                    if matches!(kind, DefKind::Macro(..)) {
-                        // FIXME: Macros are currently encoded twice, once as items and once as
-                        // reexports. We ignore the items here and only use the reexports.
-                        continue;
-                    }
                     let def_id = self.local_def_id(child_index);
                     let res = Res::Def(kind, def_id);
                     let vis = self.get_visibility(child_index);
                     let span = self.get_span(child_index, sess);
+                    let macro_rules = match kind {
+                        DefKind::Macro(..) => match self.kind(child_index) {
+                            EntryKind::MacroDef(_, macro_rules) => macro_rules,
+                            _ => unreachable!(),
+                        },
+                        _ => false,
+                    };
 
-                    callback(ModChild { ident, res, vis, span });
+                    callback(ModChild { ident, res, vis, span, macro_rules });
 
                     // For non-re-export structs and variants add their constructors to children.
                     // Re-export lists automatically contain constructors when necessary.
@@ -1109,7 +1113,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                                 let ctor_res =
                                     Res::Def(DefKind::Ctor(CtorOf::Struct, ctor_kind), ctor_def_id);
                                 let vis = self.get_visibility(ctor_def_id.index);
-                                callback(ModChild { ident, res: ctor_res, vis, span });
+                                callback(ModChild {
+                                    ident,
+                                    res: ctor_res,
+                                    vis,
+                                    span,
+                                    macro_rules: false,
+                                });
                             }
                         }
                         DefKind::Variant => {
@@ -1134,7 +1144,13 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                                     vis = ty::Visibility::Restricted(crate_def_id);
                                 }
                             }
-                            callback(ModChild { ident, res: ctor_res, vis, span });
+                            callback(ModChild {
+                                ident,
+                                res: ctor_res,
+                                vis,
+                                span,
+                                macro_rules: false,
+                            });
                         }
                         _ => {}
                     }
@@ -1402,9 +1418,11 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
         tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx)))
     }
 
-    fn get_macro(self, id: DefIndex, sess: &Session) -> MacroDef {
+    fn get_macro(self, id: DefIndex, sess: &Session) -> ast::MacroDef {
         match self.kind(id) {
-            EntryKind::MacroDef(macro_def) => macro_def.decode((self, sess)),
+            EntryKind::MacroDef(mac_args, macro_rules) => {
+                ast::MacroDef { body: P(mac_args.decode((self, sess))), macro_rules }
+            }
             _ => bug!(),
         }
     }
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index da8995df1ac..fae76f80c4b 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -1406,8 +1406,8 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
 
                 EntryKind::Fn(self.lazy(data))
             }
-            hir::ItemKind::Macro(ref macro_def) => {
-                EntryKind::MacroDef(self.lazy(macro_def.clone()))
+            hir::ItemKind::Macro(ref macro_def, _) => {
+                EntryKind::MacroDef(self.lazy(&*macro_def.body), macro_def.macro_rules)
             }
             hir::ItemKind::Mod(ref m) => {
                 return self.encode_info_for_mod(item.def_id, m);
diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs
index da17d9d4c67..a30cc034c4a 100644
--- a/compiler/rustc_metadata/src/rmeta/mod.rs
+++ b/compiler/rustc_metadata/src/rmeta/mod.rs
@@ -2,7 +2,7 @@ use decoder::Metadata;
 use def_path_hash_map::DefPathHashMapRef;
 use table::{Table, TableBuilder};
 
-use rustc_ast::{self as ast, MacroDef};
+use rustc_ast as ast;
 use rustc_attr as attr;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::MetadataRef;
@@ -350,7 +350,7 @@ enum EntryKind {
     Fn(Lazy<FnData>),
     ForeignFn(Lazy<FnData>),
     Mod(Lazy<[ModChild]>),
-    MacroDef(Lazy<MacroDef>),
+    MacroDef(Lazy<ast::MacArgs>, /*macro_rules*/ bool),
     ProcMacro(MacroKind),
     Closure,
     Generator,
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index f36847c7781..ec20e888333 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -14,7 +14,6 @@ use rustc_hir::*;
 use rustc_index::vec::Idx;
 use rustc_middle::hir::nested_filter;
 use rustc_span::def_id::StableCrateId;
-use rustc_span::hygiene::MacroKind;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
 use rustc_span::Span;
@@ -232,7 +231,7 @@ impl<'hir> Map<'hir> {
                 ItemKind::Static(..) => DefKind::Static,
                 ItemKind::Const(..) => DefKind::Const,
                 ItemKind::Fn(..) => DefKind::Fn,
-                ItemKind::Macro(..) => DefKind::Macro(MacroKind::Bang),
+                ItemKind::Macro(_, macro_kind) => DefKind::Macro(macro_kind),
                 ItemKind::Mod(..) => DefKind::Mod,
                 ItemKind::OpaqueTy(..) => DefKind::OpaqueTy,
                 ItemKind::TyAlias(..) => DefKind::TyAlias,
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 294c70f24f8..35e1558600d 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -2,7 +2,7 @@ use std::cmp;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
-use rustc_errors::{DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId};
 use rustc_hir::HirId;
 use rustc_index::vec::IndexVec;
 use rustc_query_system::ich::StableHashingContext;
@@ -186,28 +186,28 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
     }
 }
 
-pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a>);
+pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a, ()>);
 
 impl<'a> LintDiagnosticBuilder<'a> {
     /// Return the inner DiagnosticBuilder, first setting the primary message to `msg`.
-    pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> {
+    pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a, ()> {
         self.0.set_primary_message(msg);
         self.0.set_is_lint();
         self.0
     }
 
     /// Create a LintDiagnosticBuilder from some existing DiagnosticBuilder.
-    pub fn new(err: DiagnosticBuilder<'a>) -> LintDiagnosticBuilder<'a> {
+    pub fn new(err: DiagnosticBuilder<'a, ()>) -> LintDiagnosticBuilder<'a> {
         LintDiagnosticBuilder(err)
     }
 }
 
-pub fn explain_lint_level_source<'s>(
-    sess: &'s Session,
+pub fn explain_lint_level_source(
+    sess: &Session,
     lint: &'static Lint,
     level: Level,
     src: LintLevelSource,
-    err: &mut DiagnosticBuilder<'s>,
+    err: &mut Diagnostic,
 ) {
     let name = lint.name_lower();
     match src {
@@ -314,10 +314,8 @@ pub fn struct_lint_level<'s, 'd>(
                     return;
                 }
             }
-            (Level::Warn, Some(span)) => sess.struct_span_warn(span, ""),
-            (Level::Warn, None) => sess.struct_warn(""),
-            (Level::ForceWarn, Some(span)) => sess.struct_span_force_warn(span, ""),
-            (Level::ForceWarn, None) => sess.struct_force_warn(""),
+            (Level::Warn | Level::ForceWarn, Some(span)) => sess.struct_span_warn(span, ""),
+            (Level::Warn | Level::ForceWarn, None) => sess.struct_warn(""),
             (Level::Deny | Level::Forbid, Some(span)) => {
                 let mut builder = sess.diagnostic().struct_err_lint("");
                 builder.set_span(span);
diff --git a/compiler/rustc_middle/src/metadata.rs b/compiler/rustc_middle/src/metadata.rs
index 6dcdc58c72d..c8e78747d8e 100644
--- a/compiler/rustc_middle/src/metadata.rs
+++ b/compiler/rustc_middle/src/metadata.rs
@@ -21,4 +21,6 @@ pub struct ModChild {
     pub vis: ty::Visibility,
     /// Span of the item.
     pub span: Span,
+    /// A proper `macro_rules` item (not a reexport).
+    pub macro_rules: bool,
 }
diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs
index 7a7d2470444..ff19c33d8e8 100644
--- a/compiler/rustc_middle/src/middle/stability.rs
+++ b/compiler/rustc_middle/src/middle/stability.rs
@@ -7,7 +7,7 @@ use crate::ty::{self, DefIdTree, TyCtxt};
 use rustc_ast::NodeId;
 use rustc_attr::{self as attr, ConstStability, Deprecation, Stability};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_feature::GateIssue;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -167,7 +167,7 @@ pub fn deprecation_in_effect(depr: &Deprecation) -> bool {
 }
 
 pub fn deprecation_suggestion(
-    diag: &mut DiagnosticBuilder<'_>,
+    diag: &mut Diagnostic,
     kind: &str,
     suggestion: Option<Symbol>,
     span: Span,
diff --git a/compiler/rustc_middle/src/mir/interpret/error.rs b/compiler/rustc_middle/src/mir/interpret/error.rs
index c5866924eda..31468ce73bf 100644
--- a/compiler/rustc_middle/src/mir/interpret/error.rs
+++ b/compiler/rustc_middle/src/mir/interpret/error.rs
@@ -36,7 +36,10 @@ TrivialTypeFoldableAndLiftImpls! {
 pub type EvalToAllocationRawResult<'tcx> = Result<ConstAlloc<'tcx>, ErrorHandled>;
 pub type EvalToConstValueResult<'tcx> = Result<ConstValue<'tcx>, ErrorHandled>;
 
-pub fn struct_error<'tcx>(tcx: TyCtxtAt<'tcx>, msg: &str) -> DiagnosticBuilder<'tcx> {
+pub fn struct_error<'tcx>(
+    tcx: TyCtxtAt<'tcx>,
+    msg: &str,
+) -> DiagnosticBuilder<'tcx, ErrorReported> {
     struct_span_err!(tcx.sess, tcx.span, E0080, "{}", msg)
 }
 
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index b54418e5201..072e6346cde 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -15,7 +15,7 @@ use crate::ty::subst::SubstsRef;
 use crate::ty::{self, AdtKind, Ty, TyCtxt};
 
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_span::symbol::Symbol;
@@ -841,7 +841,7 @@ impl ObjectSafetyViolation {
         }
     }
 
-    pub fn solution(&self, err: &mut DiagnosticBuilder<'_>) {
+    pub fn solution(&self, err: &mut Diagnostic) {
         match *self {
             ObjectSafetyViolation::SizedSelf(_) | ObjectSafetyViolation::SupertraitSelf(_) => {}
             ObjectSafetyViolation::Method(
diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs
index 934a6a878fe..58cf9fa7a89 100644
--- a/compiler/rustc_middle/src/ty/diagnostics.rs
+++ b/compiler/rustc_middle/src/ty/diagnostics.rs
@@ -7,7 +7,7 @@ use crate::ty::{
     ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
 };
 
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
@@ -129,7 +129,7 @@ impl<'tcx> Ty<'tcx> {
 
 pub fn suggest_arbitrary_trait_bound(
     generics: &hir::Generics<'_>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     param_name: &str,
     constraint: &str,
 ) -> bool {
@@ -159,7 +159,7 @@ pub fn suggest_arbitrary_trait_bound(
 
 fn suggest_removing_unsized_bound(
     generics: &hir::Generics<'_>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     param_name: &str,
     param: &hir::GenericParam<'_>,
     def_id: Option<DefId>,
@@ -266,7 +266,7 @@ fn suggest_removing_unsized_bound(
 pub fn suggest_constraining_type_param(
     tcx: TyCtxt<'_>,
     generics: &hir::Generics<'_>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     param_name: &str,
     constraint: &str,
     def_id: Option<DefId>,
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 246c53fdffe..708bd64578c 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -3,7 +3,7 @@ use crate::ty::diagnostics::suggest_constraining_type_param;
 use crate::ty::print::{FmtPrinter, Printer};
 use crate::ty::{self, BoundRegionKind, Region, Ty, TyCtxt};
 use rustc_errors::Applicability::{MachineApplicable, MaybeIncorrect};
-use rustc_errors::{pluralize, DiagnosticBuilder};
+use rustc_errors::{pluralize, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_span::symbol::{sym, Symbol};
@@ -347,7 +347,8 @@ impl<'tcx> Ty<'tcx> {
 impl<'tcx> TyCtxt<'tcx> {
     pub fn note_and_explain_type_err(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         err: &TypeError<'tcx>,
         cause: &ObligationCause<'tcx>,
         sp: Span,
@@ -584,7 +585,8 @@ impl<T> Trait<T> for X {
 
     fn suggest_constraint(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         msg: &str,
         body_owner_def_id: DefId,
         proj_ty: &ty::ProjectionTy<'tcx>,
@@ -671,7 +673,8 @@ impl<T> Trait<T> for X {
     ///    fn that returns the type.
     fn expected_projection(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         proj_ty: &ty::ProjectionTy<'tcx>,
         values: &ExpectedFound<Ty<'tcx>>,
         body_owner_def_id: DefId,
@@ -766,7 +769,8 @@ fn foo(&self) -> Self::T { String::new() }
     /// a return type. This can occur when dealing with `TryStream` (#71035).
     fn suggest_constraining_opaque_associated_type(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         msg: &str,
         proj_ty: &ty::ProjectionTy<'tcx>,
         ty: Ty<'tcx>,
@@ -802,7 +806,8 @@ fn foo(&self) -> Self::T { String::new() }
 
     fn point_at_methods_that_satisfy_associated_type(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         assoc_container_id: DefId,
         current_method_ident: Option<Symbol>,
         proj_ty_item_def_id: DefId,
@@ -857,7 +862,8 @@ fn foo(&self) -> Self::T { String::new() }
 
     fn point_at_associated_type(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         body_owner_def_id: DefId,
         found: Ty<'tcx>,
     ) -> bool {
@@ -921,7 +927,8 @@ fn foo(&self) -> Self::T { String::new() }
     /// type is defined on a supertrait of the one present in the bounds.
     fn constrain_generic_bound_associated_type_structured_suggestion(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         trait_ref: &ty::TraitRef<'tcx>,
         bounds: hir::GenericBounds<'_>,
         assoc: &ty::AssocItem,
@@ -958,7 +965,8 @@ fn foo(&self) -> Self::T { String::new() }
     /// associated type to a given type `ty`.
     fn constrain_associated_type_structured_suggestion(
         self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         span: Span,
         assoc: &ty::AssocItem,
         assoc_substs: &[ty::GenericArg<'tcx>],
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index d357ac69302..2663ed9049d 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -6,7 +6,9 @@ use super::{PatCtxt, PatternError};
 
 use rustc_arena::TypedArena;
 use rustc_ast::Mutability;
-use rustc_errors::{error_code, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+    error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def::*;
 use rustc_hir::def_id::DefId;
@@ -36,7 +38,11 @@ crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
     visitor.visit_body(tcx.hir().body(body_id));
 }
 
-fn create_e0004(sess: &Session, sp: Span, error_message: String) -> DiagnosticBuilder<'_> {
+fn create_e0004(
+    sess: &Session,
+    sp: Span,
+    error_message: String,
+) -> DiagnosticBuilder<'_, ErrorReported> {
     struct_span_err!(sess, sp, E0004, "{}", &error_message)
 }
 
@@ -281,12 +287,7 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
 
 /// A path pattern was interpreted as a constant, not a new variable.
 /// This caused an irrefutable match failure in e.g. `let`.
-fn const_not_var(
-    err: &mut DiagnosticBuilder<'_>,
-    tcx: TyCtxt<'_>,
-    pat: &Pat<'_>,
-    path: &hir::Path<'_>,
-) {
+fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) {
     let descr = path.res.descr();
     err.span_label(
         pat.span,
@@ -594,7 +595,7 @@ crate fn pattern_not_covered_label(
 /// Point at the definition of non-covered `enum` variants.
 fn adt_defined_here<'p, 'tcx>(
     cx: &MatchCheckCtxt<'p, 'tcx>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     ty: Ty<'tcx>,
     witnesses: &[DeconstructedPat<'p, 'tcx>],
 ) {
diff --git a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
index a19a3c8b1d5..8731669b109 100644
--- a/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
+++ b/compiler/rustc_mir_transform/src/check_const_item_mutation.rs
@@ -64,7 +64,7 @@ impl<'tcx> ConstMutationChecker<'_, 'tcx> {
         place: &Place<'tcx>,
         const_item: DefId,
         location: Location,
-        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b>,
+        decorate: impl for<'b> FnOnce(LintDiagnosticBuilder<'b>) -> DiagnosticBuilder<'b, ()>,
     ) {
         // Don't lint on borrowing/assigning when a dereference is involved.
         // If we 'leave' the temporary via a dereference, we must
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index a517e4879aa..2f88c45a2a3 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1270,7 +1270,10 @@ impl<'v> RootCollector<'_, 'v> {
         // late-bound regions, since late-bound
         // regions must appear in the argument
         // listing.
-        let main_ret_ty = self.tcx.erase_regions(main_ret_ty.no_bound_vars().unwrap());
+        let main_ret_ty = self.tcx.normalize_erasing_regions(
+            ty::ParamEnv::reveal_all(),
+            main_ret_ty.no_bound_vars().unwrap(),
+        );
 
         let start_instance = Instance::resolve(
             self.tcx,
diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs
index 4cdd83c0acd..3212fc39fb9 100644
--- a/compiler/rustc_parse/src/lexer/mod.rs
+++ b/compiler/rustc_parse/src/lexer/mod.rs
@@ -3,7 +3,9 @@ use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::token::{self, CommentKind, Token, TokenKind};
 use rustc_ast::tokenstream::{Spacing, TokenStream};
 use rustc_ast::util::unicode::contains_text_flow_control_chars;
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError, PResult};
+use rustc_errors::{
+    error_code, Applicability, DiagnosticBuilder, ErrorReported, FatalError, PResult,
+};
 use rustc_lexer::unescape::{self, Mode};
 use rustc_lexer::{Base, DocStyle, RawStrError};
 use rustc_session::lint::builtin::{
@@ -127,7 +129,7 @@ impl<'a> StringReader<'a> {
         to_pos: BytePos,
         m: &str,
         c: char,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         self.sess
             .span_diagnostic
             .struct_span_fatal(self.mk_sp(from_pos, to_pos), &format!("{}: {}", m, escaped_char(c)))
diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
index a41956c58f0..c7d166319ea 100644
--- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
+++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs
@@ -144,7 +144,7 @@ pub(crate) fn emit_unescape_error(
                     c.escape_default().to_string(),
                     Applicability::MachineApplicable,
                 )
-                .emit()
+                .emit();
         }
         EscapeError::BareCarriageReturn => {
             let msg = if mode.in_double_quotes() {
@@ -292,16 +292,18 @@ pub(crate) fn emit_unescape_error(
                 .span_label(span, "must have at most 6 hex digits")
                 .emit();
         }
-        EscapeError::UnclosedUnicodeEscape => handler
-            .struct_span_err(span, "unterminated unicode escape")
-            .span_label(span, "missing a closing `}`")
-            .span_suggestion_verbose(
-                span.shrink_to_hi(),
-                "terminate the unicode escape",
-                "}".to_string(),
-                Applicability::MaybeIncorrect,
-            )
-            .emit(),
+        EscapeError::UnclosedUnicodeEscape => {
+            handler
+                .struct_span_err(span, "unterminated unicode escape")
+                .span_label(span, "missing a closing `}`")
+                .span_suggestion_verbose(
+                    span.shrink_to_hi(),
+                    "terminate the unicode escape",
+                    "}".to_string(),
+                    Applicability::MaybeIncorrect,
+                )
+                .emit();
+        }
         EscapeError::NoBraceInUnicodeEscape => {
             let msg = "incorrect unicode escape sequence";
             let mut diag = handler.struct_span_err(span, msg);
@@ -347,7 +349,7 @@ pub(crate) fn emit_unescape_error(
         }
         EscapeError::ZeroChars => {
             let msg = "empty character literal";
-            handler.struct_span_err(span, msg).span_label(span, msg).emit()
+            handler.struct_span_err(span, msg).span_label(span, msg).emit();
         }
         EscapeError::LoneSlash => {
             let msg = "invalid trailing slash in literal";
diff --git a/compiler/rustc_parse/src/lexer/unicode_chars.rs b/compiler/rustc_parse/src/lexer/unicode_chars.rs
index c2a75d2bc2c..1d63b79adc5 100644
--- a/compiler/rustc_parse/src/lexer/unicode_chars.rs
+++ b/compiler/rustc_parse/src/lexer/unicode_chars.rs
@@ -3,7 +3,7 @@
 
 use super::StringReader;
 use crate::token;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_span::{symbol::kw, BytePos, Pos, Span};
 
 #[rustfmt::skip] // for line breaks
@@ -336,7 +336,7 @@ pub(super) fn check_for_substitution<'a>(
     reader: &StringReader<'a>,
     pos: BytePos,
     ch: char,
-    err: &mut DiagnosticBuilder<'a>,
+    err: &mut Diagnostic,
 ) -> Option<token::TokenKind> {
     let Some(&(_u_char, u_name, ascii_char)) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) else {
         return None;
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index ca92d6b7fd0..379e47077ea 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -3,7 +3,7 @@ use rustc_ast as ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{error_code, DiagnosticBuilder, PResult};
+use rustc_errors::{error_code, Diagnostic, PResult};
 use rustc_span::{sym, BytePos, Span};
 use std::convert::TryInto;
 
@@ -147,7 +147,7 @@ impl<'a> Parser<'a> {
 
     fn annotate_following_item_if_applicable(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         attr_type: OuterAttributeType,
     ) -> Option<Span> {
@@ -165,7 +165,7 @@ impl<'a> Parser<'a> {
         loop {
             // skip any other attributes, we want the item
             if snapshot.token.kind == token::Pound {
-                if let Err(mut err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
+                if let Err(err) = snapshot.parse_attribute(InnerAttrPolicy::Permitted) {
                     err.cancel();
                     return Some(replacement_span);
                 }
@@ -206,7 +206,7 @@ impl<'a> Parser<'a> {
                 );
                 return None;
             }
-            Err(mut item_err) => {
+            Err(item_err) => {
                 item_err.cancel();
             }
             Ok(None) => {}
@@ -412,12 +412,12 @@ impl<'a> Parser<'a> {
     fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
         match self.parse_unsuffixed_lit() {
             Ok(lit) => return Ok(ast::NestedMetaItem::Literal(lit)),
-            Err(ref mut err) => err.cancel(),
+            Err(err) => err.cancel(),
         }
 
         match self.parse_meta_item() {
             Ok(mi) => return Ok(ast::NestedMetaItem::MetaItem(mi)),
-            Err(ref mut err) => err.cancel(),
+            Err(err) => err.cancel(),
         }
 
         let found = pprust::token_to_string(&self.token);
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index def23005fbe..f1c2dcf10e8 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -16,7 +16,7 @@ use rustc_ast::{
 };
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err};
+use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorReported};
 use rustc_errors::{Applicability, DiagnosticBuilder, Handler, PResult};
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, Ident};
@@ -53,7 +53,11 @@ pub enum Error {
 }
 
 impl Error {
-    fn span_err(self, sp: impl Into<MultiSpan>, handler: &Handler) -> DiagnosticBuilder<'_> {
+    fn span_err(
+        self,
+        sp: impl Into<MultiSpan>,
+        handler: &Handler,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         match self {
             Error::UselessDocComment => {
                 let mut err = struct_span_err!(
@@ -151,11 +155,19 @@ impl AttemptLocalParseRecovery {
 }
 
 impl<'a> Parser<'a> {
-    pub(super) fn span_err<S: Into<MultiSpan>>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> {
+    pub(super) fn span_err<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        err: Error,
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         err.span_err(sp, self.diagnostic())
     }
 
-    pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, m: &str) -> DiagnosticBuilder<'a> {
+    pub fn struct_span_err<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        m: &str,
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         self.sess.span_diagnostic.struct_span_err(sp, m)
     }
 
@@ -171,7 +183,7 @@ impl<'a> Parser<'a> {
         self.sess.source_map().span_to_snippet(span)
     }
 
-    pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a> {
+    pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorReported> {
         let mut err = self.struct_span_err(
             self.token.span,
             &format!("expected identifier, found {}", super::token_descr(&self.token)),
@@ -393,7 +405,7 @@ impl<'a> Parser<'a> {
         Err(err)
     }
 
-    fn check_too_many_raw_str_terminators(&mut self, err: &mut DiagnosticBuilder<'_>) -> bool {
+    fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool {
         match (&self.prev_token.kind, &self.token.kind) {
             (
                 TokenKind::Literal(Lit {
@@ -461,12 +473,12 @@ impl<'a> Parser<'a> {
                     tail.could_be_bare_literal = true;
                     Ok(tail)
                 }
-                (Err(mut err), Ok(tail)) => {
+                (Err(err), Ok(tail)) => {
                     // We have a block tail that contains a somehow valid type ascription expr.
                     err.cancel();
                     Ok(tail)
                 }
-                (Err(mut snapshot_err), Err(err)) => {
+                (Err(snapshot_err), Err(err)) => {
                     // We don't know what went wrong, emit the normal error.
                     snapshot_err.cancel();
                     self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
@@ -483,7 +495,7 @@ impl<'a> Parser<'a> {
 
     pub fn maybe_annotate_with_ascription(
         &mut self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         maybe_expected_semicolon: bool,
     ) {
         if let Some((sp, likely_path)) = self.last_type_ascription.take() {
@@ -537,7 +549,7 @@ impl<'a> Parser<'a> {
     /// Eats and discards tokens until one of `kets` is encountered. Respects token trees,
     /// passes through any errors encountered. Used for error recovery.
     pub(super) fn eat_to_tokens(&mut self, kets: &[&TokenKind]) {
-        if let Err(ref mut err) =
+        if let Err(err) =
             self.parse_seq_to_before_tokens(kets, SeqSep::none(), TokenExpectType::Expect, |p| {
                 Ok(p.parse_token_tree())
             })
@@ -703,7 +715,7 @@ impl<'a> Parser<'a> {
                         *self = snapshot;
                     }
                 }
-                Err(mut err) => {
+                Err(err) => {
                     // We couldn't parse generic parameters, unlikely to be a turbofish. Rely on
                     // generic parse error instead.
                     err.cancel();
@@ -717,7 +729,7 @@ impl<'a> Parser<'a> {
     /// encounter a parse error when encountering the first `,`.
     pub(super) fn check_mistyped_turbofish_with_multiple_type_params(
         &mut self,
-        mut e: DiagnosticBuilder<'a>,
+        mut e: DiagnosticBuilder<'a, ErrorReported>,
         expr: &mut P<Expr>,
     ) -> PResult<'a, ()> {
         if let ExprKind::Binary(binop, _, _) = &expr.kind {
@@ -744,14 +756,14 @@ impl<'a> Parser<'a> {
                                             self.mk_expr_err(expr.span.to(self.prev_token.span));
                                         return Ok(());
                                     }
-                                    Err(mut err) => {
+                                    Err(err) => {
                                         *expr = self.mk_expr_err(expr.span);
                                         err.cancel();
                                     }
                                 }
                             }
                         }
-                        Err(mut err) => {
+                        Err(err) => {
                             err.cancel();
                         }
                         _ => {}
@@ -767,7 +779,7 @@ impl<'a> Parser<'a> {
     /// parenthesising the leftmost comparison.
     fn attempt_chained_comparison_suggestion(
         &mut self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         inner_op: &Expr,
         outer_op: &Spanned<AssocOp>,
     ) -> bool /* advanced the cursor */ {
@@ -821,7 +833,7 @@ impl<'a> Parser<'a> {
                             enclose(r1.span, r2.span);
                             true
                         }
-                        Err(mut expr_err) => {
+                        Err(expr_err) => {
                             expr_err.cancel();
                             *self = snapshot;
                             false
@@ -838,7 +850,7 @@ impl<'a> Parser<'a> {
                             enclose(l1.span, r1.span);
                             true
                         }
-                        Err(mut expr_err) => {
+                        Err(expr_err) => {
                             expr_err.cancel();
                             *self = snapshot;
                             false
@@ -890,7 +902,7 @@ impl<'a> Parser<'a> {
                     "comparison operators cannot be chained",
                 );
 
-                let suggest = |err: &mut DiagnosticBuilder<'_>| {
+                let suggest = |err: &mut Diagnostic| {
                     err.span_suggestion_verbose(
                         op.span.shrink_to_lo(),
                         TURBOFISH_SUGGESTION_STR,
@@ -938,7 +950,7 @@ impl<'a> Parser<'a> {
                                 // `ExprKind::Err` placeholder.
                                 mk_err_expr(self, inner_op.span.to(self.prev_token.span))
                             }
-                            Err(mut expr_err) => {
+                            Err(expr_err) => {
                                 expr_err.cancel();
                                 // Not entirely sure now, but we bubble the error up with the
                                 // suggestion.
@@ -1439,7 +1451,7 @@ impl<'a> Parser<'a> {
     pub(super) fn recover_closing_delimiter(
         &mut self,
         tokens: &[TokenKind],
-        mut err: DiagnosticBuilder<'a>,
+        mut err: DiagnosticBuilder<'a, ErrorReported>,
     ) -> PResult<'a, bool> {
         let mut pos = None;
         // We want to use the last closing delim that would apply.
@@ -1637,7 +1649,7 @@ impl<'a> Parser<'a> {
 
     pub(super) fn parameter_without_type(
         &mut self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         pat: P<ast::Pat>,
         require_name: bool,
         first_param: bool,
@@ -1810,7 +1822,7 @@ impl<'a> Parser<'a> {
         }
     }
 
-    pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a> {
+    pub(super) fn expected_expression_found(&self) -> DiagnosticBuilder<'a, ErrorReported> {
         let (span, msg) = match (&self.token.kind, self.subparser_name) {
             (&token::Eof, Some(origin)) => {
                 let sp = self.sess.source_map().next_point(self.prev_token.span);
@@ -1946,17 +1958,14 @@ impl<'a> Parser<'a> {
         Ok(expr)
     }
 
-    fn recover_const_param_decl(
-        &mut self,
-        ty_generics: Option<&Generics>,
-    ) -> PResult<'a, Option<GenericArg>> {
+    fn recover_const_param_decl(&mut self, ty_generics: Option<&Generics>) -> Option<GenericArg> {
         let snapshot = self.clone();
         let param = match self.parse_const_param(vec![]) {
             Ok(param) => param,
-            Err(mut err) => {
+            Err(err) => {
                 err.cancel();
                 *self = snapshot;
-                return Err(err);
+                return None;
             }
         };
         let mut err =
@@ -1977,7 +1986,7 @@ impl<'a> Parser<'a> {
         }
         let value = self.mk_expr_err(param.span());
         err.emit();
-        return Ok(Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })));
+        Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }))
     }
 
     pub fn recover_const_param_declaration(
@@ -1985,8 +1994,8 @@ impl<'a> Parser<'a> {
         ty_generics: Option<&Generics>,
     ) -> PResult<'a, Option<GenericArg>> {
         // We have to check for a few different cases.
-        if let Ok(arg) = self.recover_const_param_decl(ty_generics) {
-            return Ok(arg);
+        if let Some(arg) = self.recover_const_param_decl(ty_generics) {
+            return Ok(Some(arg));
         }
 
         // We haven't consumed `const` yet.
@@ -2019,7 +2028,7 @@ impl<'a> Parser<'a> {
     pub fn recover_const_arg(
         &mut self,
         start: Span,
-        mut err: DiagnosticBuilder<'a>,
+        mut err: DiagnosticBuilder<'a, ErrorReported>,
     ) -> PResult<'a, GenericArg> {
         let is_op = AssocOp::from_token(&self.token)
             .and_then(|op| {
@@ -2085,7 +2094,7 @@ impl<'a> Parser<'a> {
                     return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }));
                 }
             }
-            Err(mut err) => {
+            Err(err) => {
                 err.cancel();
             }
         }
@@ -2099,7 +2108,7 @@ impl<'a> Parser<'a> {
     pub(super) fn incorrect_move_async_order_found(
         &self,
         move_async_span: Span,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let mut err =
             self.struct_span_err(move_async_span, "the order of `move` and `async` is incorrect");
         err.span_suggestion_verbose(
@@ -2139,7 +2148,7 @@ impl<'a> Parser<'a> {
             Err(mut err) => {
                 self.bump(); // Skip the `:`.
                 match self.parse_pat_no_top_alt(expected) {
-                    Err(mut inner_err) => {
+                    Err(inner_err) => {
                         // Carry on as if we had not done anything, callers will emit a
                         // reasonable error.
                         inner_err.cancel();
@@ -2246,7 +2255,7 @@ impl<'a> Parser<'a> {
         // suggestion-enhanced error here rather than choking on the comma later.
         let comma_span = self.token.span;
         self.bump();
-        if let Err(mut err) = self.skip_pat_list() {
+        if let Err(err) = self.skip_pat_list() {
             // We didn't expect this to work anyway; we just wanted to advance to the
             // end of the comma-sequence so we know the span to suggest parenthesizing.
             err.cancel();
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 7cb8c35b868..c6919779ffd 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -17,7 +17,7 @@ use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported, PResult};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
 use rustc_span::edition::LATEST_STABLE_EDITION;
@@ -684,7 +684,7 @@ impl<'a> Parser<'a> {
         let parser_snapshot_before_type = self.clone();
         let cast_expr = match self.parse_as_cast_ty() {
             Ok(rhs) => mk_expr(self, lhs, rhs),
-            Err(mut type_err) => {
+            Err(type_err) => {
                 // Rewind to before attempting to parse the type with generics, to recover
                 // from situations like `x as usize < y` in which we first tried to parse
                 // `usize < y` as a type with generic arguments.
@@ -717,7 +717,7 @@ impl<'a> Parser<'a> {
                                     .emit();
                                 return Ok(expr);
                             }
-                            Err(mut err) => {
+                            Err(err) => {
                                 err.cancel();
                                 *self = snapshot;
                             }
@@ -773,7 +773,7 @@ impl<'a> Parser<'a> {
 
                         expr
                     }
-                    Err(mut path_err) => {
+                    Err(path_err) => {
                         // Couldn't parse as a path, return original error and parser state.
                         path_err.cancel();
                         *self = parser_snapshot_after_type;
@@ -1127,7 +1127,7 @@ impl<'a> Parser<'a> {
         snapshot: Option<(Self, ExprKind)>,
     ) -> Option<P<Expr>> {
         match (seq.as_mut(), snapshot) {
-            (Err(ref mut err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
+            (Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
                 let name = pprust::path_to_string(&path);
                 snapshot.bump(); // `(`
                 match snapshot.parse_struct_fields(path, false, token::Paren) {
@@ -1138,11 +1138,12 @@ impl<'a> Parser<'a> {
                         let close_paren = self.prev_token.span;
                         let span = lo.to(self.prev_token.span);
                         if !fields.is_empty() {
-                            err.cancel();
-                            let mut err = self.struct_span_err(
+                            let replacement_err = self.struct_span_err(
                                 span,
                                 "invalid `struct` delimiters or `fn` call arguments",
                             );
+                            mem::replace(err, replacement_err).cancel();
+
                             err.multipart_suggestion(
                                 &format!("if `{}` is a struct, use braces as delimiters", name),
                                 vec![
@@ -1166,7 +1167,9 @@ impl<'a> Parser<'a> {
                         return Some(self.mk_expr_err(span));
                     }
                     Ok(_) => {}
-                    Err(mut err) => err.emit(),
+                    Err(mut err) => {
+                        err.emit();
+                    }
                 }
             }
             _ => {}
@@ -1622,9 +1625,11 @@ impl<'a> Parser<'a> {
                 };
                 if let Some(expr) = expr {
                     if matches!(expr.kind, ExprKind::Err) {
-                        self.diagnostic()
-                            .delay_span_bug(self.token.span, &"invalid interpolated expression");
-                        return self.diagnostic().struct_dummy();
+                        let mut err = self
+                            .diagnostic()
+                            .struct_span_err(self.token.span, &"invalid interpolated expression");
+                        err.downgrade_to_delayed_bug();
+                        return err;
                     }
                 }
             }
@@ -1816,6 +1821,7 @@ impl<'a> Parser<'a> {
                 err
             } else {
                 self.struct_span_err(sp, &format!("suffixes on {} are invalid", kind))
+                    .forget_guarantee()
             };
             err.span_label(sp, format!("invalid suffix `{}`", suf));
             err.emit();
@@ -1876,7 +1882,7 @@ impl<'a> Parser<'a> {
                 *self = snapshot;
                 Some(self.mk_expr_err(arr.span))
             }
-            Err(mut e) => {
+            Err(e) => {
                 e.cancel();
                 None
             }
@@ -2097,9 +2103,9 @@ impl<'a> Parser<'a> {
     fn error_missing_if_then_block(
         &self,
         if_span: Span,
-        err: Option<DiagnosticBuilder<'a>>,
+        err: Option<DiagnosticBuilder<'a, ErrorReported>>,
         binop_span: Option<Span>,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let msg = "this `if` expression has a condition, but no block";
 
         let mut err = if let Some(mut err) = err {
@@ -2379,7 +2385,7 @@ impl<'a> Parser<'a> {
                         return Some(err(self, stmts));
                     }
                 }
-                Err(mut err) => {
+                Err(err) => {
                     err.cancel();
                 }
             }
@@ -2396,7 +2402,7 @@ impl<'a> Parser<'a> {
                 }
                 // We couldn't parse either yet another statement missing it's
                 // enclosing block nor the next arm's pattern or closing brace.
-                Err(mut stmt_err) => {
+                Err(stmt_err) => {
                     stmt_err.cancel();
                     *self = start_snapshot;
                     break;
@@ -2653,7 +2659,7 @@ impl<'a> Parser<'a> {
         let mut base = ast::StructRest::None;
         let mut recover_async = false;
 
-        let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| {
+        let mut async_block_err = |e: &mut Diagnostic, span: Span| {
             recover_async = true;
             e.span_label(span, "`async` blocks are only allowed in Rust 2018 or later");
             e.help(&format!("set `edition = \"{}\"` in `Cargo.toml`", LATEST_STABLE_EDITION));
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 4b57aa1f24a..1b9eeab0298 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -130,7 +130,7 @@ impl<'a> Parser<'a> {
                                 // FIXME - try to continue parsing other generics?
                                 return Ok((None, TrailingToken::None));
                             }
-                            Err(mut err) => {
+                            Err(err) => {
                                 err.cancel();
                                 // FIXME - maybe we should overwrite 'self' outside of `collect_tokens`?
                                 *this = snapshot;
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 20ca8a99ab7..bd349e89482 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -13,7 +13,7 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari
 use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
 use rustc_ast::{MacArgs, MacCall, MacDelimiter};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, PResult, StashKey};
+use rustc_errors::{struct_span_err, Applicability, ErrorReported, PResult, StashKey};
 use rustc_span::edition::{Edition, LATEST_STABLE_EDITION};
 use rustc_span::lev_distance::lev_distance;
 use rustc_span::source_map::{self, Span};
@@ -801,7 +801,7 @@ impl<'a> Parser<'a> {
         before_where_clause_span: Span,
         after_predicates: &[WherePredicate],
         after_where_clause_span: Span,
-    ) {
+    ) -> ErrorReported {
         let mut err =
             self.struct_span_err(after_where_clause_span, "where clause not allowed here");
         if !after_predicates.is_empty() {
@@ -1114,7 +1114,7 @@ impl<'a> Parser<'a> {
         // Only try to recover if this is implementing a trait for a type
         let mut impl_info = match self.parse_item_impl(attrs, defaultness) {
             Ok(impl_info) => impl_info,
-            Err(mut recovery_error) => {
+            Err(recovery_error) => {
                 // Recovery failed, raise the "expected identifier" error
                 recovery_error.cancel();
                 return Err(err);
@@ -1476,7 +1476,9 @@ impl<'a> Parser<'a> {
                             // after the comma
                             self.eat(&token::Comma);
                             // `check_trailing_angle_brackets` already emitted a nicer error
-                            err.cancel();
+                            // NOTE(eddyb) this was `.cancel()`, but `err`
+                            // gets returned, so we can't fully defuse it.
+                            err.downgrade_to_delayed_bug();
                         }
                     }
                 }
@@ -2073,7 +2075,7 @@ impl<'a> Parser<'a> {
                         if let Ok(snippet) = self.span_to_snippet(sp) {
                             let current_vis = match self.parse_visibility(FollowedByType::No) {
                                 Ok(v) => v,
-                                Err(mut d) => {
+                                Err(d) => {
                                     d.cancel();
                                     return Err(err);
                                 }
@@ -2216,7 +2218,7 @@ impl<'a> Parser<'a> {
                     // If this is a C-variadic argument and we hit an error, return the error.
                     Err(err) if this.token == token::DotDotDot => return Err(err),
                     // Recover from attempting to parse the argument as a type without pattern.
-                    Err(mut err) => {
+                    Err(err) => {
                         err.cancel();
                         *this = parser_snapshot_before_ty;
                         this.recover_arg_parse()?
@@ -2358,7 +2360,7 @@ impl<'a> Parser<'a> {
         match self
             .parse_outer_attributes()
             .and_then(|_| self.parse_self_param())
-            .map_err(|mut e| e.cancel())
+            .map_err(|e| e.cancel())
         {
             Ok(Some(_)) => "method",
             _ => "function",
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 6d534bece46..d8e6d5037bb 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -32,7 +32,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::PResult;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported, FatalError};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{MultiSpan, Span, DUMMY_SP};
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -849,7 +849,7 @@ impl<'a> Parser<'a> {
                                     v.push(t);
                                     continue;
                                 }
-                                Err(mut e) => {
+                                Err(e) => {
                                     // Parsing failed, therefore it must be something more serious
                                     // than just a missing separator.
                                     expect_err.emit();
@@ -877,7 +877,7 @@ impl<'a> Parser<'a> {
     fn recover_missing_braces_around_closure_body(
         &mut self,
         closure_spans: ClosureSpans,
-        mut expect_err: DiagnosticBuilder<'_>,
+        mut expect_err: DiagnosticBuilder<'_, ErrorReported>,
     ) -> PResult<'a, ()> {
         let initial_semicolon = self.token.span;
 
@@ -1429,7 +1429,7 @@ impl<'a> Parser<'a> {
 crate fn make_unclosed_delims_error(
     unmatched: UnmatchedBrace,
     sess: &ParseSess,
-) -> Option<DiagnosticBuilder<'_>> {
+) -> Option<DiagnosticBuilder<'_, ErrorReported>> {
     // `None` here means an `Eof` was found. We already emit those errors elsewhere, we add them to
     // `unmatched_braces` only for error recovery in the `Parser`.
     let found_delim = unmatched.found_delim?;
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index ac3123c40e3..d4129871145 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -8,7 +8,7 @@ use rustc_ast::{
     PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
 };
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, PResult};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported, PResult};
 use rustc_span::source_map::{respan, Span, Spanned};
 use rustc_span::symbol::{kw, sym, Ident};
 
@@ -655,7 +655,7 @@ impl<'a> Parser<'a> {
 
     fn fatal_unexpected_non_pat(
         &mut self,
-        mut err: DiagnosticBuilder<'a>,
+        err: DiagnosticBuilder<'a, ErrorReported>,
         expected: Expected,
     ) -> PResult<'a, P<Pat>> {
         err.cancel();
@@ -722,7 +722,7 @@ impl<'a> Parser<'a> {
             // Ensure the user doesn't receive unhelpful unexpected token errors
             self.bump();
             if self.is_pat_range_end_start(0) {
-                let _ = self.parse_pat_range_end().map_err(|mut e| e.cancel());
+                let _ = self.parse_pat_range_end().map_err(|e| e.cancel());
             }
 
             self.error_inclusive_range_with_extra_equals(span_with_eq);
@@ -886,7 +886,7 @@ impl<'a> Parser<'a> {
         let mut fields = Vec::new();
         let mut etc = false;
         let mut ate_comma = true;
-        let mut delayed_err: Option<DiagnosticBuilder<'a>> = None;
+        let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorReported>> = None;
         let mut etc_span = None;
 
         while self.token != token::CloseDelim(token::Brace) {
diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs
index 48502112e3a..b5857e05970 100644
--- a/compiler/rustc_parse/src/parser/path.rs
+++ b/compiler/rustc_parse/src/parser/path.rs
@@ -394,7 +394,7 @@ impl<'a> Parser<'a> {
         debug!("parse_generic_args_with_leading_angle_bracket_recovery: (snapshotting)");
         match self.parse_angle_args(ty_generics) {
             Ok(args) => Ok(args),
-            Err(mut e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
+            Err(e) if is_first_invocation && self.unmatched_angle_bracket_count > 0 => {
                 // Swap `self` with our backup of the parser state before attempting to parse
                 // generic arguments.
                 let snapshot = mem::replace(self, snapshot.unwrap());
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 965e6a6ca3f..6b195285243 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -18,7 +18,7 @@ use rustc_ast::{
 };
 use rustc_ast::{Block, BlockCheckMode, Expr, ExprKind, Local, Stmt};
 use rustc_ast::{StmtKind, DUMMY_NODE_ID};
-use rustc_errors::{Applicability, DiagnosticBuilder, PResult};
+use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported, PResult};
 use rustc_span::source_map::{BytePos, Span};
 use rustc_span::symbol::{kw, sym};
 
@@ -296,7 +296,7 @@ impl<'a> Parser<'a> {
                 // extra noise.
                 init
             }
-            (Err(mut init_err), Some((snapshot, _, ty_err))) => {
+            (Err(init_err), Some((snapshot, _, ty_err))) => {
                 // init error, ty error
                 init_err.cancel();
                 // Couldn't parse the type nor the initializer, only raise the type error and
@@ -414,7 +414,10 @@ impl<'a> Parser<'a> {
         Ok(block)
     }
 
-    fn error_block_no_opening_brace_msg(&mut self, msg: &str) -> DiagnosticBuilder<'a> {
+    fn error_block_no_opening_brace_msg(
+        &mut self,
+        msg: &str,
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let sp = self.token.span;
         let mut e = self.struct_span_err(sp, msg);
         let do_not_suggest_help = self.token.is_keyword(kw::In) || self.token == token::Colon;
@@ -449,7 +452,7 @@ impl<'a> Parser<'a> {
                     );
                 }
             }
-            Err(mut e) => {
+            Err(e) => {
                 self.recover_stmt_(SemiColonMode::Break, BlockMode::Ignore);
                 e.cancel();
             }
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 566b77a5e9e..0b01f9e927f 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -345,7 +345,8 @@ impl<'a> Parser<'a> {
         let lt_no_plus = self.check_lifetime() && !self.look_ahead(1, |t| t.is_like_plus());
         let bounds = self.parse_generic_bounds_common(allow_plus, None)?;
         if lt_no_plus {
-            self.struct_span_err(lo, "lifetime in trait object type must be followed by `+`").emit()
+            self.struct_span_err(lo, "lifetime in trait object type must be followed by `+`")
+                .emit();
         }
         Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
     }
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 3d69e8ba4e4..68efbbb74c3 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -1951,7 +1951,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
         // Historically we've run more checks on non-exported than exported macros,
         // so this lets us continue to run them while maintaining backwards compatibility.
         // In the long run, the checks should be harmonized.
-        if let ItemKind::Macro(ref macro_def) = item.kind {
+        if let ItemKind::Macro(ref macro_def, _) = item.kind {
             let def_id = item.def_id.to_def_id();
             if macro_def.macro_rules && !self.tcx.has_attr(def_id, sym::macro_export) {
                 check_non_exported_macro_for_invalid_attrs(self.tcx, item);
@@ -2095,7 +2095,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
                             rustc_errors::Applicability::MachineApplicable,
                         );
                     }
-                    err.emit()
+                    err.emit();
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 2b11f6b0c1d..0fdbdb7b08d 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -219,7 +219,9 @@ impl<'tcx> CheckConstVisitor<'tcx> {
             required_gates.iter().copied().filter(|&g| !features.enabled(g)).collect();
 
         match missing_gates.as_slice() {
-            [] => struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit(),
+            [] => {
+                struct_span_err!(tcx.sess, span, E0744, "{}", msg).emit();
+            }
 
             [missing_primary, ref missing_secondary @ ..] => {
                 let mut err = feature_err(&tcx.sess.parse_sess, *missing_primary, span, &msg);
diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs
index d7dde157864..b2129ce9f24 100644
--- a/compiler/rustc_passes/src/intrinsicck.rs
+++ b/compiler/rustc_passes/src/intrinsicck.rs
@@ -118,7 +118,7 @@ impl<'tcx> ExprVisitor<'tcx> {
             err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from)))
                 .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to)));
         }
-        err.emit()
+        err.emit();
     }
 
     fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs
index 48594e73f5b..3dd9995fa00 100644
--- a/compiler/rustc_privacy/src/lib.rs
+++ b/compiler/rustc_privacy/src/lib.rs
@@ -564,7 +564,7 @@ impl<'tcx> EmbargoVisitor<'tcx> {
             // privacy and mark them reachable.
             DefKind::Macro(_) => {
                 let item = self.tcx.hir().expect_item(def_id);
-                if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }) = item.kind {
+                if let hir::ItemKind::Macro(MacroDef { macro_rules: false, .. }, _) = item.kind {
                     if vis.is_accessible_from(module.to_def_id(), self.tcx) {
                         self.update(def_id, level);
                     }
@@ -686,7 +686,7 @@ impl<'tcx> Visitor<'tcx> for EmbargoVisitor<'tcx> {
                     }
                 }
             }
-            hir::ItemKind::Macro(ref macro_def) => {
+            hir::ItemKind::Macro(ref macro_def, _) => {
                 self.update_reachability_from_macro(item.def_id, macro_def);
             }
             hir::ItemKind::ForeignMod { items, .. } => {
diff --git a/compiler/rustc_query_system/src/query/config.rs b/compiler/rustc_query_system/src/query/config.rs
index b1ff1e15a9d..91b6ea03835 100644
--- a/compiler/rustc_query_system/src/query/config.rs
+++ b/compiler/rustc_query_system/src/query/config.rs
@@ -7,7 +7,7 @@ use crate::query::caches::QueryCache;
 use crate::query::{QueryCacheStore, QueryContext, QueryState};
 
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::{DiagnosticBuilder, ErrorReported};
 use std::fmt::Debug;
 use std::hash::Hash;
 
@@ -27,7 +27,7 @@ pub struct QueryVtable<CTX: QueryContext, K, V> {
 
     pub compute: fn(CTX::DepContext, K) -> V,
     pub hash_result: Option<fn(&mut StableHashingContext<'_>, &V) -> Fingerprint>,
-    pub handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V,
+    pub handle_cycle_error: fn(CTX, DiagnosticBuilder<'_, ErrorReported>) -> V,
     pub try_load_from_disk: Option<fn(CTX, SerializedDepNodeIndex) -> Option<V>>,
 }
 
diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs
index 4588403925e..260fc3bff44 100644
--- a/compiler/rustc_query_system/src/query/job.rs
+++ b/compiler/rustc_query_system/src/query/job.rs
@@ -4,7 +4,7 @@ use crate::query::{QueryContext, QueryStackFrame};
 use rustc_hir::def::DefKind;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, Handler, Level};
+use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorReported, Handler, Level};
 use rustc_session::Session;
 use rustc_span::Span;
 
@@ -530,7 +530,7 @@ pub fn deadlock<CTX: QueryContext>(tcx: CTX, registry: &rayon_core::Registry) {
 pub(crate) fn report_cycle<'a>(
     sess: &'a Session,
     CycleError { usage, cycle: stack }: CycleError,
-) -> DiagnosticBuilder<'a> {
+) -> DiagnosticBuilder<'a, ErrorReported> {
     assert!(!stack.is_empty());
 
     let fix_span = |span: Span, query: &QueryStackFrame| {
diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs
index 77e1fd3f2cc..23df0d4dcd4 100644
--- a/compiler/rustc_query_system/src/query/plumbing.rs
+++ b/compiler/rustc_query_system/src/query/plumbing.rs
@@ -14,7 +14,7 @@ use rustc_data_structures::profiling::TimingGuard;
 use rustc_data_structures::sharded::{get_shard_index_by_hash, Sharded};
 use rustc_data_structures::sync::{Lock, LockGuard};
 use rustc_data_structures::thin_vec::ThinVec;
-use rustc_errors::{DiagnosticBuilder, FatalError};
+use rustc_errors::{DiagnosticBuilder, ErrorReported, FatalError};
 use rustc_session::Session;
 use rustc_span::{Span, DUMMY_SP};
 use std::cell::Cell;
@@ -143,7 +143,7 @@ where
 fn mk_cycle<CTX, V, R>(
     tcx: CTX,
     error: CycleError,
-    handle_cycle_error: fn(CTX, DiagnosticBuilder<'_>) -> V,
+    handle_cycle_error: fn(CTX, DiagnosticBuilder<'_, ErrorReported>) -> V,
     cache: &dyn crate::query::QueryStorage<Value = V, Stored = R>,
 ) -> R
 where
diff --git a/compiler/rustc_resolve/src/access_levels.rs b/compiler/rustc_resolve/src/access_levels.rs
index 60cc4248edc..61a9b644cb8 100644
--- a/compiler/rustc_resolve/src/access_levels.rs
+++ b/compiler/rustc_resolve/src/access_levels.rs
@@ -133,7 +133,7 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
             ast::ItemKind::Impl(..) => return,
 
             // Only exported `macro_rules!` items are public, but they always are
-            ast::ItemKind::MacroDef(..) => {
+            ast::ItemKind::MacroDef(ref macro_def) if macro_def.macro_rules => {
                 let is_macro_export =
                     item.attrs.iter().any(|attr| attr.has_name(sym::macro_export));
                 if is_macro_export { Some(AccessLevel::Public) } else { None }
@@ -155,7 +155,8 @@ impl<'r, 'ast> Visitor<'ast> for AccessLevelsVisitor<'ast, 'r> {
             | ast::ItemKind::Struct(..)
             | ast::ItemKind::Union(..)
             | ast::ItemKind::Trait(..)
-            | ast::ItemKind::TraitAlias(..) => {
+            | ast::ItemKind::TraitAlias(..)
+            | ast::ItemKind::MacroDef(..) => {
                 if item.vis.kind.is_pub() {
                     self.prev_level
                 } else {
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 3fa9343c399..2c3ddae9cb4 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -940,7 +940,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
     /// Builds the reduced graph for a single item in an external crate.
     fn build_reduced_graph_for_external_crate_res(&mut self, child: ModChild) {
         let parent = self.parent_scope.module;
-        let ModChild { ident, res, vis, span } = child;
+        let ModChild { ident, res, vis, span, macro_rules } = child;
         let res = res.expect_non_local();
         let expansion = self.parent_scope.expansion;
         // Record primary definitions.
@@ -972,7 +972,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 _,
             ) => self.r.define(parent, ident, ValueNS, (res, vis, span, expansion)),
             Res::Def(DefKind::Macro(..), _) | Res::NonMacroAttr(..) => {
-                self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
+                if !macro_rules {
+                    self.r.define(parent, ident, MacroNS, (res, vis, span, expansion))
+                }
             }
             Res::Def(
                 DefKind::TyParam
@@ -1068,8 +1070,9 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                             .emit();
                     }
                 }
-                let ill_formed =
-                    |span| struct_span_err!(self.r.session, span, E0466, "bad macro import").emit();
+                let ill_formed = |span| {
+                    struct_span_err!(self.r.session, span, E0466, "bad macro import").emit();
+                };
                 match attr.meta() {
                     Some(meta) => match meta.kind {
                         MetaItemKind::Word => {
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 9a2fb3b86e2..b366473cb30 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -3,7 +3,7 @@ use std::ptr;
 use rustc_ast::{self as ast, Path};
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_feature::BUILTIN_ATTRIBUTES;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind};
@@ -110,7 +110,7 @@ impl<'a> Resolver<'a> {
         &self,
         span: Span,
         resolution_error: ResolutionError<'_>,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         match resolution_error {
             ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
                 let mut err = struct_span_err!(
@@ -624,7 +624,10 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    crate fn report_vis_error(&self, vis_resolution_error: VisResolutionError<'_>) {
+    crate fn report_vis_error(
+        &self,
+        vis_resolution_error: VisResolutionError<'_>,
+    ) -> ErrorReported {
         match vis_resolution_error {
             VisResolutionError::Relative2018(span, path) => {
                 let mut err = self.session.struct_span_err(
@@ -1031,7 +1034,7 @@ impl<'a> Resolver<'a> {
 
     crate fn unresolved_macro_suggestions(
         &mut self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         macro_kind: MacroKind,
         parent_scope: &ParentScope<'a>,
         ident: Ident,
@@ -1120,7 +1123,7 @@ impl<'a> Resolver<'a> {
 
     crate fn add_typo_suggestion(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         suggestion: Option<TypoSuggestion>,
         span: Span,
     ) -> bool {
@@ -1817,7 +1820,7 @@ fn find_span_immediately_after_crate_name(
 crate fn show_candidates(
     definitions: &rustc_hir::definitions::Definitions,
     session: &Session,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     // This is `None` if all placement locations are inside expansions
     use_placement_span: Option<Span>,
     candidates: &[ImportSuggestion],
diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs
index 5e21161f2e0..bf570fb0f80 100644
--- a/compiler/rustc_resolve/src/imports.rs
+++ b/compiler/rustc_resolve/src/imports.rs
@@ -1399,14 +1399,22 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
         let mut reexports = Vec::new();
 
         module.for_each_child(self.r, |_, ident, _, binding| {
-            // Filter away ambiguous imports and anything that has def-site hygiene.
-            // FIXME: Implement actual cross-crate hygiene.
-            let is_good_import =
-                binding.is_import() && !binding.is_ambiguity() && !ident.span.from_expansion();
-            if is_good_import || binding.is_macro_def() {
+            // FIXME: Consider changing the binding inserted by `#[macro_export] macro_rules`
+            // into the crate root to actual `NameBindingKind::Import`.
+            if binding.is_import()
+                || matches!(binding.kind, NameBindingKind::Res(_, _is_macro_export @ true))
+            {
                 let res = binding.res().expect_non_local();
-                if res != def::Res::Err {
-                    reexports.push(ModChild { ident, res, vis: binding.vis, span: binding.span });
+                // Ambiguous imports are treated as errors at this point and are
+                // not exposed to other crates (see #36837 for more details).
+                if res != def::Res::Err && !binding.is_ambiguity() {
+                    reexports.push(ModChild {
+                        ident,
+                        res,
+                        vis: binding.vis,
+                        span: binding.span,
+                        macro_rules: false,
+                    });
                 }
             }
         });
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 9ac3e6e22bd..91695257137 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -2001,13 +2001,11 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
             // into a single one.
             let mut parent_err = this.r.into_struct_error(parent_err.span, parent_err.node);
 
-            parent_err.cancel();
-
             err.message = take(&mut parent_err.message);
             err.code = take(&mut parent_err.code);
             err.children = take(&mut parent_err.children);
 
-            drop(parent_err);
+            parent_err.cancel();
 
             let def_id = this.parent_scope.module.nearest_parent_mod();
 
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index f20cf29cc89..b71776c1615 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -12,7 +12,9 @@ use rustc_ast::{
 };
 use rustc_ast_pretty::pprust::path_segment_to_string;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
@@ -133,7 +135,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         span: Span,
         source: PathSource<'_>,
         res: Option<Res>,
-    ) -> (DiagnosticBuilder<'a>, Vec<ImportSuggestion>) {
+    ) -> (DiagnosticBuilder<'a, ErrorReported>, Vec<ImportSuggestion>) {
         let ident_span = path.last().map_or(span, |ident| ident.ident.span);
         let ns = source.namespace();
         let is_expected = &|res| source.is_expected(res);
@@ -606,11 +608,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         (err, candidates)
     }
 
-    fn detect_assoct_type_constraint_meant_as_path(
-        &self,
-        base_span: Span,
-        err: &mut DiagnosticBuilder<'_>,
-    ) {
+    fn detect_assoct_type_constraint_meant_as_path(&self, base_span: Span, err: &mut Diagnostic) {
         let Some(ty) = self.diagnostic_metadata.current_type_path else { return; };
         let TyKind::Path(_, path) = &ty.kind else { return; };
         for segment in &path.segments {
@@ -675,11 +673,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     }
 
     /// Given `where <T as Bar>::Baz: String`, suggest `where T: Bar<Baz = String>`.
-    fn restrict_assoc_type_in_where_clause(
-        &mut self,
-        span: Span,
-        err: &mut DiagnosticBuilder<'_>,
-    ) -> bool {
+    fn restrict_assoc_type_in_where_clause(&mut self, span: Span, err: &mut Diagnostic) -> bool {
         // Detect that we are actually in a `where` predicate.
         let (bounded_ty, bounds, where_span) =
             if let Some(ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
@@ -875,7 +869,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     /// Returns `true` if able to provide context-dependent help.
     fn smart_resolve_context_dependent_help(
         &mut self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         span: Span,
         source: PathSource<'_>,
         res: Res,
@@ -885,7 +879,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         let ns = source.namespace();
         let is_expected = &|res| source.is_expected(res);
 
-        let path_sep = |err: &mut DiagnosticBuilder<'_>, expr: &Expr| match expr.kind {
+        let path_sep = |err: &mut Diagnostic, expr: &Expr| match expr.kind {
             ExprKind::Field(_, ident) => {
                 err.span_suggestion(
                     expr.span,
@@ -908,7 +902,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             _ => false,
         };
 
-        let find_span = |source: &PathSource<'_>, err: &mut DiagnosticBuilder<'_>| {
+        let find_span = |source: &PathSource<'_>, err: &mut Diagnostic| {
             match source {
                 PathSource::Expr(Some(Expr { span, kind: ExprKind::Call(_, _), .. }))
                 | PathSource::TupleStruct(span, _) => {
@@ -1066,7 +1060,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                     })
                     .unwrap_or(false)
                 {
-                    err.delay_as_bug();
+                    err.downgrade_to_delayed_bug();
                     // We already suggested changing `:` into `::` during parsing.
                     return false;
                 }
@@ -1435,7 +1429,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
         start.to(sm.next_point(start))
     }
 
-    fn type_ascription_suggestion(&self, err: &mut DiagnosticBuilder<'_>, base_span: Span) -> bool {
+    fn type_ascription_suggestion(&self, err: &mut Diagnostic, base_span: Span) -> bool {
         let sm = self.r.session.source_map();
         let base_snippet = sm.span_to_snippet(base_span);
         if let Some(&sp) = self.diagnostic_metadata.current_type_ascription.last() {
@@ -1472,7 +1466,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
                                 .borrow_mut()
                                 .insert(colon_sp)
                             {
-                                err.delay_as_bug();
+                                err.downgrade_to_delayed_bug();
                             }
                         }
                         if let Ok(base_snippet) = base_snippet {
@@ -1577,7 +1571,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
     /// Adds a suggestion for using an enum's variant when an enum is used instead.
     fn suggest_using_enum_variant(
         &mut self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         source: PathSource<'_>,
         def_id: DefId,
         span: Span,
@@ -1825,7 +1819,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
         &self,
         spans: Vec<Span>,
         count: usize,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         struct_span_err!(
             self.tcx.sess,
             spans,
@@ -1910,7 +1904,8 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
     /// Returns whether to add `'static` lifetime to the suggested lifetime list.
     crate fn report_elision_failure(
         &mut self,
-        db: &mut DiagnosticBuilder<'_>,
+        // FIXME(eddyb) rename this since it's no longer a `DiagnosticBuilder`.
+        db: &mut Diagnostic,
         params: &[ElisionFailureInfo],
     ) -> bool {
         let mut m = String::new();
@@ -2059,7 +2054,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
 
     crate fn add_missing_lifetime_specifiers_label(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         mut spans_with_counts: Vec<(Span, usize)>,
         lifetime_names: &FxHashSet<Symbol>,
         lifetime_spans: Vec<Span>,
@@ -2090,7 +2085,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
         }
 
         let suggest_existing =
-            |err: &mut DiagnosticBuilder<'_>,
+            |err: &mut Diagnostic,
              name: &str,
              formatters: Vec<Option<Box<dyn Fn(&str) -> String>>>| {
                 if let Some(MissingLifetimeSpot::HigherRanked { span: for_span, span_type }) =
@@ -2174,7 +2169,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                     Applicability::MaybeIncorrect,
                 );
             };
-        let suggest_new = |err: &mut DiagnosticBuilder<'_>, suggs: Vec<Option<String>>| {
+        let suggest_new = |err: &mut Diagnostic, suggs: Vec<Option<String>>| {
             for missing in self.missing_named_lifetime_spots.iter().rev() {
                 let mut introduce_suggestion = vec![];
                 let msg;
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 2f0ad60709d..23a8189f62f 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -9,7 +9,7 @@
 use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
 use rustc_ast::walk_list;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefIdMap, LocalDefId};
@@ -1572,6 +1572,7 @@ fn signal_shadowing_problem(tcx: TyCtxt<'_>, name: Symbol, orig: Original, shado
             name,
             orig.kind.desc()
         )
+        .forget_guarantee()
     } else {
         // shadowing involving a label is only a warning, due to issues with
         // labels and lifetimes not being macro-hygienic.
@@ -1873,7 +1874,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
     // or from `fn rah<'a>(T<'a>)` to `fn rah(T<'_>)`
     fn suggest_eliding_single_use_lifetime(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         def_id: DefId,
         lifetime: &hir::Lifetime,
     ) {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 04b0a18b12b..4823b889207 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -40,7 +40,7 @@ use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::intern::Interned;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
 use rustc_hir::def::Namespace::*;
 use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
@@ -713,7 +713,7 @@ struct PrivacyError<'a> {
 }
 
 struct UseError<'a> {
-    err: DiagnosticBuilder<'a>,
+    err: DiagnosticBuilder<'a, ErrorReported>,
     /// Candidates which user could `use` to access the missing type.
     candidates: Vec<ImportSuggestion>,
     /// The `DefId` of the module to place the use-statements in.
@@ -845,10 +845,6 @@ impl<'a> NameBinding<'a> {
         )
     }
 
-    fn is_macro_def(&self) -> bool {
-        matches!(self.kind, NameBindingKind::Res(Res::Def(DefKind::Macro(..), _), _))
-    }
-
     fn macro_kind(&self) -> Option<MacroKind> {
         self.res().macro_kind()
     }
@@ -990,6 +986,9 @@ pub struct Resolver<'a> {
     crate_loader: CrateLoader<'a>,
     macro_names: FxHashSet<Ident>,
     builtin_macros: FxHashMap<Symbol, BuiltinMacroState>,
+    /// A small map keeping true kinds of built-in macros that appear to be fn-like on
+    /// the surface (`macro` items in libcore), but are actually attributes or derives.
+    builtin_macro_kinds: FxHashMap<LocalDefId, MacroKind>,
     registered_attrs: FxHashSet<Ident>,
     registered_tools: RegisteredTools,
     macro_use_prelude: FxHashMap<Symbol, &'a NameBinding<'a>>,
@@ -1261,6 +1260,10 @@ impl ResolverAstLowering for Resolver<'_> {
 
         def_id
     }
+
+    fn decl_macro_kind(&self, def_id: LocalDefId) -> MacroKind {
+        self.builtin_macro_kinds.get(&def_id).copied().unwrap_or(MacroKind::Bang)
+    }
 }
 
 impl<'a> Resolver<'a> {
@@ -1381,6 +1384,7 @@ impl<'a> Resolver<'a> {
             crate_loader: CrateLoader::new(session, metadata_loader, crate_name),
             macro_names: FxHashSet::default(),
             builtin_macros: Default::default(),
+            builtin_macro_kinds: Default::default(),
             registered_attrs,
             registered_tools,
             macro_use_prelude: FxHashMap::default(),
@@ -3169,7 +3173,7 @@ impl<'a> Resolver<'a> {
     /// ```
     fn add_suggestion_for_rename_of_use(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         name: Symbol,
         import: &Import<'_>,
         binding_span: Span,
@@ -3248,7 +3252,7 @@ impl<'a> Resolver<'a> {
     /// as characters expected by span manipulations won't be present.
     fn add_suggestion_for_duplicate_nested_use(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         import: &Import<'_>,
         binding_span: Span,
     ) {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 89c2a0c74bd..e34d3e605ec 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -1209,7 +1209,13 @@ impl<'a> Resolver<'a> {
                 // while still taking everything else from the source code.
                 // If we already loaded this builtin macro, give a better error message than 'no such builtin macro'.
                 match mem::replace(builtin_macro, BuiltinMacroState::AlreadySeen(item.span)) {
-                    BuiltinMacroState::NotYetSeen(ext) => result.kind = ext,
+                    BuiltinMacroState::NotYetSeen(ext) => {
+                        result.kind = ext;
+                        if item.id != ast::DUMMY_NODE_ID {
+                            self.builtin_macro_kinds
+                                .insert(self.local_def_id(item.id), result.macro_kind());
+                        }
+                    }
                     BuiltinMacroState::AlreadySeen(span) => {
                         struct_span_err!(
                             self.session,
diff --git a/compiler/rustc_save_analysis/src/sig.rs b/compiler/rustc_save_analysis/src/sig.rs
index 3bb1d2ff357..8f50f445719 100644
--- a/compiler/rustc_save_analysis/src/sig.rs
+++ b/compiler/rustc_save_analysis/src/sig.rs
@@ -416,7 +416,7 @@ impl<'hir> Sig for hir::Item<'hir> {
 
                 Ok(sig)
             }
-            hir::ItemKind::Macro(_) => {
+            hir::ItemKind::Macro(..) => {
                 let mut text = "macro".to_owned();
                 let name = self.ident.to_string();
                 text.push_str(&name);
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 7a0d9a212c9..f9b75690e37 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -8,7 +8,7 @@ use crate::search_paths::SearchPath;
 use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{early_error, early_warn, Session};
 
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::impl_stable_hash_via_hash;
 
 use rustc_target::abi::{Align, TargetDataLayout};
@@ -1023,34 +1023,30 @@ pub fn to_crate_config(cfg: FxHashSet<(String, Option<String>)>) -> CrateConfig
 
 /// The parsed `--check-cfg` options
 pub struct CheckCfg<T = String> {
-    /// Set if `names()` checking is enabled
-    pub names_checked: bool,
-    /// The union of all `names()`
-    pub names_valid: FxHashSet<T>,
-    /// The set of names for which `values()` was used
-    pub values_checked: FxHashSet<T>,
-    /// The set of all (name, value) pairs passed in `values()`
-    pub values_valid: FxHashSet<(T, T)>,
+    /// The set of all `names()`, if None no name checking is performed
+    pub names_valid: Option<FxHashSet<T>>,
+    /// The set of all `values()`
+    pub values_valid: FxHashMap<T, FxHashSet<T>>,
 }
 
 impl<T> Default for CheckCfg<T> {
     fn default() -> Self {
-        CheckCfg {
-            names_checked: false,
-            names_valid: FxHashSet::default(),
-            values_checked: FxHashSet::default(),
-            values_valid: FxHashSet::default(),
-        }
+        CheckCfg { names_valid: Default::default(), values_valid: Default::default() }
     }
 }
 
 impl<T> CheckCfg<T> {
     fn map_data<O: Eq + Hash>(&self, f: impl Fn(&T) -> O) -> CheckCfg<O> {
         CheckCfg {
-            names_checked: self.names_checked,
-            names_valid: self.names_valid.iter().map(|a| f(a)).collect(),
-            values_checked: self.values_checked.iter().map(|a| f(a)).collect(),
-            values_valid: self.values_valid.iter().map(|(a, b)| (f(a), f(b))).collect(),
+            names_valid: self
+                .names_valid
+                .as_ref()
+                .map(|names_valid| names_valid.iter().map(|a| f(a)).collect()),
+            values_valid: self
+                .values_valid
+                .iter()
+                .map(|(a, b)| (f(a), b.iter().map(|b| f(b)).collect()))
+                .collect(),
         }
     }
 }
@@ -1090,17 +1086,23 @@ impl CrateCheckConfig {
             sym::doctest,
             sym::feature,
         ];
-        for &name in WELL_KNOWN_NAMES {
-            self.names_valid.insert(name);
+        if let Some(names_valid) = &mut self.names_valid {
+            for &name in WELL_KNOWN_NAMES {
+                names_valid.insert(name);
+            }
         }
     }
 
     /// Fills a `CrateCheckConfig` with configuration names and values that are actually active.
     pub fn fill_actual(&mut self, cfg: &CrateConfig) {
         for &(k, v) in cfg {
-            self.names_valid.insert(k);
+            if let Some(names_valid) = &mut self.names_valid {
+                names_valid.insert(k);
+            }
             if let Some(v) = v {
-                self.values_valid.insert((k, v));
+                self.values_valid.entry(k).and_modify(|values| {
+                    values.insert(v);
+                });
             }
         }
     }
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 7113f9b0a2f..e287764a52a 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -7,7 +7,7 @@ use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
-use rustc_errors::{error_code, Applicability, DiagnosticBuilder};
+use rustc_errors::{error_code, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::ExpnId;
@@ -82,7 +82,7 @@ pub fn feature_err<'a>(
     feature: Symbol,
     span: impl Into<MultiSpan>,
     explain: &str,
-) -> DiagnosticBuilder<'a> {
+) -> DiagnosticBuilder<'a, ErrorReported> {
     feature_err_issue(sess, feature, span, GateIssue::Language, explain)
 }
 
@@ -96,7 +96,7 @@ pub fn feature_err_issue<'a>(
     span: impl Into<MultiSpan>,
     issue: GateIssue,
     explain: &str,
-) -> DiagnosticBuilder<'a> {
+) -> DiagnosticBuilder<'a, ErrorReported> {
     let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
 
     if let Some(n) = find_feature_issue(feature, issue) {
@@ -243,7 +243,7 @@ impl ParseSess {
 
     /// Extend an error with a suggestion to wrap an expression with parentheses to allow the
     /// parser to continue parsing the following operation as part of the same expression.
-    pub fn expr_parentheses_needed(&self, err: &mut DiagnosticBuilder<'_>, span: Span) {
+    pub fn expr_parentheses_needed(&self, err: &mut Diagnostic, span: Span) {
         err.multipart_suggestion(
             "parentheses are required to parse this as an expression",
             vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), ")".to_string())],
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 9bcdd7f3da6..b72be735ce4 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -19,7 +19,7 @@ use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitterWriter;
 use rustc_errors::emitter::{Emitter, EmitterWriter, HumanReadableErrorType};
 use rustc_errors::json::JsonEmitter;
 use rustc_errors::registry::Registry;
-use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorReported};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
 use rustc_span::edition::Edition;
@@ -221,7 +221,7 @@ enum DiagnosticBuilderMethod {
 pub trait SessionDiagnostic<'a> {
     /// Write out as a diagnostic out of `sess`.
     #[must_use]
-    fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a>;
+    fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a, ErrorReported>;
 }
 
 /// Diagnostic message ID, used by `Session.one_time_diagnostics` to avoid
@@ -303,37 +303,39 @@ impl Session {
         self.crate_types.set(crate_types).expect("`crate_types` was initialized twice")
     }
 
-    pub fn struct_span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
-        self.diagnostic().struct_span_warn(sp, msg)
-    }
-    pub fn struct_span_force_warn<S: Into<MultiSpan>>(
+    pub fn struct_span_warn<S: Into<MultiSpan>>(
         &self,
         sp: S,
         msg: &str,
-    ) -> DiagnosticBuilder<'_> {
-        self.diagnostic().struct_span_force_warn(sp, msg)
+    ) -> DiagnosticBuilder<'_, ()> {
+        self.diagnostic().struct_span_warn(sp, msg)
     }
     pub fn struct_span_warn_with_code<S: Into<MultiSpan>>(
         &self,
         sp: S,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_span_warn_with_code(sp, msg, code)
     }
-    pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_warn(msg)
     }
-    pub fn struct_force_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
-        self.diagnostic().struct_force_warn(msg)
-    }
-    pub fn struct_span_allow<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_span_allow<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        msg: &str,
+    ) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_span_allow(sp, msg)
     }
-    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_allow(msg)
     }
-    pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_span_err<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        msg: &str,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_span_err(sp, msg)
     }
     pub fn struct_span_err_with_code<S: Into<MultiSpan>>(
@@ -341,17 +343,25 @@ impl Session {
         sp: S,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_span_err_with_code(sp, msg, code)
     }
     // FIXME: This method should be removed (every error should have an associated error code).
-    pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_err(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_err(msg)
     }
-    pub fn struct_err_with_code(&self, msg: &str, code: DiagnosticId) -> DiagnosticBuilder<'_> {
+    pub fn struct_err_with_code(
+        &self,
+        msg: &str,
+        code: DiagnosticId,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_err_with_code(msg, code)
     }
-    pub fn struct_span_fatal<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_span_fatal<S: Into<MultiSpan>>(
+        &self,
+        sp: S,
+        msg: &str,
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_span_fatal(sp, msg)
     }
     pub fn struct_span_fatal_with_code<S: Into<MultiSpan>>(
@@ -359,10 +369,10 @@ impl Session {
         sp: S,
         msg: &str,
         code: DiagnosticId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_span_fatal_with_code(sp, msg, code)
     }
-    pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_fatal(&self, msg: &str) -> DiagnosticBuilder<'_, ErrorReported> {
         self.diagnostic().struct_fatal(msg)
     }
 
@@ -396,7 +406,7 @@ impl Session {
     pub fn err(&self, msg: &str) {
         self.diagnostic().err(msg)
     }
-    pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) {
+    pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorReported {
         err.into_diagnostic(self).emit()
     }
     #[inline]
@@ -467,7 +477,7 @@ impl Session {
     pub fn span_note_without_error<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
         self.diagnostic().span_note_without_error(sp, msg)
     }
-    pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_> {
+    pub fn struct_note_without_error(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
         self.diagnostic().struct_note_without_error(msg)
     }
 
@@ -478,9 +488,9 @@ impl Session {
 
     /// Analogous to calling methods on the given `DiagnosticBuilder`, but
     /// deduplicates on lint ID, span (if any), and message for this `Session`
-    fn diag_once<'a, 'b>(
-        &'a self,
-        diag_builder: &'b mut DiagnosticBuilder<'a>,
+    fn diag_once(
+        &self,
+        diag: &mut Diagnostic,
         method: DiagnosticBuilderMethod,
         msg_id: DiagnosticMessageId,
         message: &str,
@@ -491,39 +501,33 @@ impl Session {
         if fresh {
             match method {
                 DiagnosticBuilderMethod::Note => {
-                    diag_builder.note(message);
+                    diag.note(message);
                 }
                 DiagnosticBuilderMethod::SpanNote => {
                     let span = span_maybe.expect("`span_note` needs a span");
-                    diag_builder.span_note(span, message);
+                    diag.span_note(span, message);
                 }
             }
         }
     }
 
-    pub fn diag_span_note_once<'a, 'b>(
-        &'a self,
-        diag_builder: &'b mut DiagnosticBuilder<'a>,
+    pub fn diag_span_note_once(
+        &self,
+        diag: &mut Diagnostic,
         msg_id: DiagnosticMessageId,
         span: Span,
         message: &str,
     ) {
-        self.diag_once(
-            diag_builder,
-            DiagnosticBuilderMethod::SpanNote,
-            msg_id,
-            message,
-            Some(span),
-        );
+        self.diag_once(diag, DiagnosticBuilderMethod::SpanNote, msg_id, message, Some(span));
     }
 
-    pub fn diag_note_once<'a, 'b>(
-        &'a self,
-        diag_builder: &'b mut DiagnosticBuilder<'a>,
+    pub fn diag_note_once(
+        &self,
+        diag: &mut Diagnostic,
         msg_id: DiagnosticMessageId,
         message: &str,
     ) {
-        self.diag_once(diag_builder, DiagnosticBuilderMethod::Note, msg_id, message, None);
+        self.diag_once(diag, DiagnosticBuilderMethod::Note, msg_id, message, None);
     }
 
     #[inline]
diff --git a/compiler/rustc_trait_selection/src/traits/coherence.rs b/compiler/rustc_trait_selection/src/traits/coherence.rs
index 8b76f3f7151..0f6997054b9 100644
--- a/compiler/rustc_trait_selection/src/traits/coherence.rs
+++ b/compiler/rustc_trait_selection/src/traits/coherence.rs
@@ -14,6 +14,7 @@ use crate::traits::{
     PredicateObligations, SelectionContext,
 };
 //use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::Diagnostic;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::CRATE_HIR_ID;
 use rustc_infer::infer::TyCtxtInferExt;
@@ -50,7 +51,7 @@ pub struct OverlapResult<'tcx> {
     pub involves_placeholder: bool,
 }
 
-pub fn add_placeholder_note(err: &mut rustc_errors::DiagnosticBuilder<'_>) {
+pub fn add_placeholder_note(err: &mut Diagnostic) {
     err.note(
         "this behavior recently changed as a result of a bug fix; \
          see rust-lang/rust#56105 for details",
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 8746d66ebb6..f26f32aabda 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -12,7 +12,9 @@ use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCod
 use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use crate::infer::{self, InferCtxt, TyCtxtInferExt};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::Visitor;
@@ -100,7 +102,7 @@ pub trait InferCtxtExt<'tcx> {
         expected_args: Vec<ArgKind>,
         found_args: Vec<ArgKind>,
         is_closure: bool,
-    ) -> DiagnosticBuilder<'tcx>;
+    ) -> DiagnosticBuilder<'tcx, ErrorReported>;
 }
 
 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
@@ -1017,7 +1019,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         expected_args: Vec<ArgKind>,
         found_args: Vec<ArgKind>,
         is_closure: bool,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let kind = if is_closure { "closure" } else { "function" };
 
         let args_str = |arguments: &[ArgKind], other: &[ArgKind]| {
@@ -1174,7 +1176,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
     fn report_similar_impl_candidates(
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     );
 
     /// Gets the parent trait chain start
@@ -1186,11 +1188,7 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        trait_ref: &ty::PolyTraitRef<'tcx>,
-    );
+    fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>);
 
     /// Creates a `PredicateObligation` with `new_self_ty` replacing the existing type in the
     /// `trait_ref`.
@@ -1215,35 +1213,26 @@ trait InferCtxtPrivExt<'hir, 'tcx> {
         pred: ty::PolyTraitRef<'tcx>,
     ) -> bool;
 
-    fn note_obligation_cause(
-        &self,
-        err: &mut DiagnosticBuilder<'tcx>,
-        obligation: &PredicateObligation<'tcx>,
-    );
+    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>);
 
     fn suggest_unsized_bound_if_applicable(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     );
 
     fn annotate_source_of_ambiguity(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         impls: &[DefId],
         predicate: ty::Predicate<'tcx>,
     );
 
-    fn maybe_suggest_unsized_generics(
-        &self,
-        err: &mut DiagnosticBuilder<'tcx>,
-        span: Span,
-        node: Node<'hir>,
-    );
+    fn maybe_suggest_unsized_generics(&self, err: &mut Diagnostic, span: Span, node: Node<'hir>);
 
     fn maybe_indirection_for_unsized(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         item: &'hir Item<'hir>,
         param: &'hir GenericParam<'hir>,
     ) -> bool;
@@ -1572,7 +1561,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
     fn report_similar_impl_candidates(
         &self,
         impl_candidates: Vec<ImplCandidate<'tcx>>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     ) {
         if impl_candidates.is_empty() {
             return;
@@ -1649,11 +1638,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
     /// If the `Self` type of the unsatisfied trait `trait_ref` implements a trait
     /// with the same path as `trait_ref`, a help message about
     /// a probable version mismatch is added to `err`
-    fn note_version_mismatch(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        trait_ref: &ty::PolyTraitRef<'tcx>,
-    ) {
+    fn note_version_mismatch(&self, err: &mut Diagnostic, trait_ref: &ty::PolyTraitRef<'tcx>) {
         let get_trait_impl = |trait_def_id| {
             self.tcx.find_map_relevant_impl(trait_def_id, trait_ref.skip_binder().self_ty(), Some)
         };
@@ -1944,7 +1929,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
 
     fn annotate_source_of_ambiguity(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         impls: &[DefId],
         predicate: ty::Predicate<'tcx>,
     ) {
@@ -1977,7 +1962,9 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
             // Avoid complaining about other inference issues for expressions like
             // `42 >> 1`, where the types are still `{integer}`, but we want to
             // Do we need `trait_ref.skip_binder().self_ty().is_numeric() &&` too?
-            err.cancel();
+            // NOTE(eddyb) this was `.cancel()`, but `err`
+            // is borrowed, so we can't fully defuse it.
+            err.downgrade_to_delayed_bug();
             return;
         }
         let post = if post.len() > 4 {
@@ -2088,11 +2075,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
         })
     }
 
-    fn note_obligation_cause(
-        &self,
-        err: &mut DiagnosticBuilder<'tcx>,
-        obligation: &PredicateObligation<'tcx>,
-    ) {
+    fn note_obligation_cause(&self, err: &mut Diagnostic, obligation: &PredicateObligation<'tcx>) {
         // First, attempt to add note to this error with an async-await-specific
         // message, and fall back to regular note otherwise.
         if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
@@ -2110,7 +2093,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
 
     fn suggest_unsized_bound_if_applicable(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     ) {
         let (pred, item_def_id, span) = match (
@@ -2139,7 +2122,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
 
     fn maybe_suggest_unsized_generics<'hir>(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         span: Span,
         node: Node<'hir>,
     ) {
@@ -2206,7 +2189,7 @@ impl<'a, 'tcx> InferCtxtPrivExt<'a, 'tcx> for InferCtxt<'a, 'tcx> {
 
     fn maybe_indirection_for_unsized<'hir>(
         &self,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         item: &'hir Item<'hir>,
         param: &'hir GenericParam<'hir>,
     ) -> bool {
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index e162e943e36..c4fbd25b833 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -10,7 +10,8 @@ use crate::traits::normalize_projection_type;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::{
-    error_code, pluralize, struct_span_err, Applicability, DiagnosticBuilder, Style,
+    error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
+    ErrorReported, Style,
 };
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
@@ -47,7 +48,7 @@ pub enum GeneratorInteriorOrUpvar {
 pub trait InferCtxtExt<'tcx> {
     fn suggest_restricting_param_bound(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         body_id: hir::HirId,
     );
@@ -55,28 +56,23 @@ pub trait InferCtxtExt<'tcx> {
     fn suggest_dereferences(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
 
-    fn get_closure_name(
-        &self,
-        def_id: DefId,
-        err: &mut DiagnosticBuilder<'_>,
-        msg: &str,
-    ) -> Option<String>;
+    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
 
     fn suggest_fn_call(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
 
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         has_custom_message: bool,
     ) -> bool;
@@ -84,27 +80,23 @@ pub trait InferCtxtExt<'tcx> {
     fn suggest_remove_reference(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
 
-    fn suggest_remove_await(
-        &self,
-        obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
-    );
+    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
 
     fn suggest_change_mut(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
 
     fn suggest_semicolon_removal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     );
@@ -113,7 +105,7 @@ pub trait InferCtxtExt<'tcx> {
 
     fn suggest_impl_trait(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -121,7 +113,7 @@ pub trait InferCtxtExt<'tcx> {
 
     fn point_at_returns_when_relevant(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     );
 
@@ -131,11 +123,11 @@ pub trait InferCtxtExt<'tcx> {
         found_span: Option<Span>,
         expected_ref: ty::PolyTraitRef<'tcx>,
         found: ty::PolyTraitRef<'tcx>,
-    ) -> DiagnosticBuilder<'tcx>;
+    ) -> DiagnosticBuilder<'tcx, ErrorReported>;
 
     fn suggest_fully_qualified_path(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         def_id: DefId,
         span: Span,
         trait_ref: DefId,
@@ -143,13 +135,13 @@ pub trait InferCtxtExt<'tcx> {
 
     fn maybe_note_obligation_cause_for_async_await(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     ) -> bool;
 
     fn note_obligation_cause_for_async_await(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         interior_or_upvar_span: GeneratorInteriorOrUpvar,
         interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
         inner_generator_body: Option<&hir::Body<'tcx>>,
@@ -163,7 +155,7 @@ pub trait InferCtxtExt<'tcx> {
 
     fn note_obligation_cause_code<T>(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         predicate: &T,
         param_env: ty::ParamEnv<'tcx>,
         cause_code: &ObligationCauseCode<'tcx>,
@@ -172,12 +164,12 @@ pub trait InferCtxtExt<'tcx> {
     ) where
         T: fmt::Display;
 
-    fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>);
+    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
 
     /// Suggest to await before try: future? => future.await?
     fn suggest_await_before_try(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
@@ -202,7 +194,7 @@ fn suggest_restriction<'tcx>(
     tcx: TyCtxt<'tcx>,
     generics: &hir::Generics<'tcx>,
     msg: &str,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     fn_sig: Option<&hir::FnSig<'_>>,
     projection: Option<&ty::ProjectionTy<'_>>,
     trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -329,7 +321,7 @@ fn suggest_restriction<'tcx>(
 impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_restricting_param_bound(
         &self,
-        mut err: &mut DiagnosticBuilder<'_>,
+        mut err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         body_id: hir::HirId,
     ) {
@@ -493,7 +485,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_dereferences(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'tcx>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         // It only make sense when suggesting dereferences for arguments
@@ -549,26 +541,20 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     /// Given a closure's `DefId`, return the given name of the closure.
     ///
     /// This doesn't account for reassignments, but it's only used for suggestions.
-    fn get_closure_name(
-        &self,
-        def_id: DefId,
-        err: &mut DiagnosticBuilder<'_>,
-        msg: &str,
-    ) -> Option<String> {
-        let get_name =
-            |err: &mut DiagnosticBuilder<'_>, kind: &hir::PatKind<'_>| -> Option<String> {
-                // Get the local name of this closure. This can be inaccurate because
-                // of the possibility of reassignment, but this should be good enough.
-                match &kind {
-                    hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
-                        Some(format!("{}", name))
-                    }
-                    _ => {
-                        err.note(&msg);
-                        None
-                    }
+    fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String> {
+        let get_name = |err: &mut Diagnostic, kind: &hir::PatKind<'_>| -> Option<String> {
+            // Get the local name of this closure. This can be inaccurate because
+            // of the possibility of reassignment, but this should be good enough.
+            match &kind {
+                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, _, name, None) => {
+                    Some(format!("{}", name))
                 }
-            };
+                _ => {
+                    err.note(&msg);
+                    None
+                }
+            }
+        };
 
         let hir = self.tcx.hir();
         let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?);
@@ -590,7 +576,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_fn_call(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let self_ty = match trait_pred.self_ty().no_bound_vars() {
@@ -683,7 +669,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_add_reference_to_arg(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         poly_trait_pred: ty::PolyTraitPredicate<'tcx>,
         has_custom_message: bool,
     ) -> bool {
@@ -817,7 +803,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_remove_reference(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let span = obligation.cause.span;
@@ -874,11 +860,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_remove_await(
-        &self,
-        obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
-    ) {
+    fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
         let span = obligation.cause.span;
 
         if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() {
@@ -936,7 +918,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_change_mut(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
         let points_at_arg = matches!(
@@ -1012,7 +994,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     fn suggest_semicolon_removal(
         &self,
         obligation: &PredicateObligation<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
     ) {
@@ -1063,7 +1045,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     /// emitted.
     fn suggest_impl_trait(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
@@ -1256,7 +1238,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
     fn point_at_returns_when_relevant(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     ) {
         match obligation.cause.code().peel_derives() {
@@ -1290,7 +1272,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         found_span: Option<Span>,
         expected_ref: ty::PolyTraitRef<'tcx>,
         found: ty::PolyTraitRef<'tcx>,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         crate fn build_fn_sig_string<'tcx>(
             tcx: TyCtxt<'tcx>,
             trait_ref: ty::PolyTraitRef<'tcx>,
@@ -1345,7 +1327,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
     fn suggest_fully_qualified_path(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         def_id: DefId,
         span: Span,
         trait_ref: DefId,
@@ -1411,7 +1393,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     /// Returns `true` if an async-await specific note was added to the diagnostic.
     fn maybe_note_obligation_cause_for_async_await(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
     ) -> bool {
         debug!(
@@ -1639,7 +1621,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
     /// `maybe_note_obligation_cause_for_async_await`'s documentation comment.
     fn note_obligation_cause_for_async_await(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         interior_or_upvar_span: GeneratorInteriorOrUpvar,
         interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
         inner_generator_body: Option<&hir::Body<'tcx>>,
@@ -1896,7 +1878,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
     fn note_obligation_cause_code<T>(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         predicate: &T,
         param_env: ty::ParamEnv<'tcx>,
         cause_code: &ObligationCauseCode<'tcx>,
@@ -2133,7 +2115,9 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
                 let ty = parent_trait_ref.skip_binder().self_ty();
                 if parent_trait_ref.references_error() {
-                    err.cancel();
+                    // NOTE(eddyb) this was `.cancel()`, but `err`
+                    // is borrowed, so we can't fully defuse it.
+                    err.downgrade_to_delayed_bug();
                     return;
                 }
 
@@ -2412,7 +2396,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         }
     }
 
-    fn suggest_new_overflow_limit(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
         let suggested_limit = match self.tcx.recursion_limit() {
             Limit(0) => Limit(2),
             limit => limit * 2,
@@ -2427,7 +2411,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
 
     fn suggest_await_before_try(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         obligation: &PredicateObligation<'tcx>,
         trait_pred: ty::PolyTraitPredicate<'tcx>,
         span: Span,
@@ -2615,7 +2599,7 @@ impl NextTypeParamName for &[hir::GenericParam<'_>] {
 }
 
 fn suggest_trait_object_return_type_alternatives(
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     ret_ty: Span,
     trait_obj: &str,
     is_object_safe: bool,
diff --git a/compiler/rustc_trait_selection/src/traits/misc.rs b/compiler/rustc_trait_selection/src/traits/misc.rs
index b23dce8a581..c293708dcc9 100644
--- a/compiler/rustc_trait_selection/src/traits/misc.rs
+++ b/compiler/rustc_trait_selection/src/traits/misc.rs
@@ -20,6 +20,7 @@ pub fn can_type_implement_copy<'tcx>(
     tcx: TyCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
     self_type: Ty<'tcx>,
+    cause: ObligationCause<'tcx>,
 ) -> Result<(), CopyImplementationError<'tcx>> {
     // FIXME: (@jroesch) float this code up
     tcx.infer_ctxt().enter(|infcx| {
@@ -49,7 +50,19 @@ pub fn can_type_implement_copy<'tcx>(
                     continue;
                 }
                 let span = tcx.def_span(field.did);
-                let cause = ObligationCause::dummy_with_span(span);
+                // FIXME(compiler-errors): This gives us better spans for bad
+                // projection types like in issue-50480.
+                // If the ADT has substs, point to the cause we are given.
+                // If it does not, then this field probably doesn't normalize
+                // to begin with, and point to the bad field's span instead.
+                let cause = if field
+                    .ty(tcx, traits::InternalSubsts::identity_for_item(tcx, adt.did))
+                    .has_param_types_or_consts()
+                {
+                    cause.clone()
+                } else {
+                    ObligationCause::dummy_with_span(span)
+                };
                 let ctx = traits::FulfillmentContext::new();
                 match traits::fully_normalize(&infcx, ctx, cause, param_env, ty) {
                     Ok(ty) => {
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5b646c6d447..8bcb1ccb584 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -29,7 +29,7 @@ use crate::traits::project::ProjectionCacheKeyExt;
 use crate::traits::ProjectionCacheKey;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
-use rustc_errors::ErrorReported;
+use rustc_errors::{Diagnostic, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::LateBoundRegionConversionTime;
@@ -64,7 +64,7 @@ pub enum IntercrateAmbiguityCause {
 impl IntercrateAmbiguityCause {
     /// Emits notes when the overlap is caused by complex intercrate ambiguities.
     /// See #23980 for details.
-    pub fn add_intercrate_ambiguity_hint(&self, err: &mut rustc_errors::DiagnosticBuilder<'_>) {
+    pub fn add_intercrate_ambiguity_hint(&self, err: &mut Diagnostic) {
         err.note(&self.intercrate_ambiguity_hint());
     }
 
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 38a6220082f..acf1c976afc 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -450,7 +450,7 @@ fn report_conflicting_impls(
             sg.has_errored = true;
             if overlap.with_impl.is_local() || !tcx.orphan_check_crate(()).contains(&impl_def_id) {
                 let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
-                decorate(LintDiagnosticBuilder::new(err));
+                decorate(LintDiagnosticBuilder::new(err.forget_guarantee()));
             } else {
                 tcx.sess.delay_span_bug(impl_span, "impl should have failed the orphan check");
             }
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index b6e653c0eea..f800e7a1402 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -1,4 +1,4 @@
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_span::Span;
 use smallvec::smallvec;
 use smallvec::SmallVec;
@@ -43,12 +43,7 @@ impl<'tcx> TraitAliasExpansionInfo<'tcx> {
 
     /// Adds diagnostic labels to `diag` for the expansion path of a trait through all intermediate
     /// trait aliases.
-    pub fn label_with_exp_info(
-        &self,
-        diag: &mut DiagnosticBuilder<'_>,
-        top_label: &str,
-        use_desc: &str,
-    ) {
+    pub fn label_with_exp_info(&self, diag: &mut Diagnostic, top_label: &str, use_desc: &str) {
         diag.span_label(self.top().1, top_label);
         if self.path.len() > 1 {
             for (_, sp) in self.path.iter().rev().skip(1).take(self.path.len() - 2) {
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 05ff7f818c7..b16bf33f06a 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -6,7 +6,7 @@ use crate::astconv::{
 use crate::errors::AssocTypeBindingNotAllowed;
 use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
 use rustc_ast::ast::ParamKindOrd;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -49,7 +49,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
         }
 
-        let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut DiagnosticBuilder<'_>| {
+        let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| {
             let suggestions = vec![
                 (arg.span().shrink_to_lo(), String::from("{ ")),
                 (arg.span().shrink_to_hi(), String::from(" }")),
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index dbe7ddeb6a8..efa50375c95 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -15,7 +15,7 @@ use crate::middle::resolve_lifetime as rl;
 use crate::require_c_abi_if_c_variadic;
 use rustc_ast::TraitObjectSyntax;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{struct_span_err, Applicability, ErrorReported, FatalError};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported, FatalError};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
@@ -2618,7 +2618,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         &self,
         constrained_regions: FxHashSet<ty::BoundRegionKind>,
         referenced_regions: FxHashSet<ty::BoundRegionKind>,
-        generate_err: impl Fn(&str) -> rustc_errors::DiagnosticBuilder<'tcx>,
+        generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx, ErrorReported>,
     ) {
         for br in referenced_regions.difference(&constrained_regions) {
             let br_name = match *br {
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 3701b255b75..9529e1528a8 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -1,6 +1,6 @@
 use crate::check::coercion::{AsCoercionSite, CoerceMany};
 use crate::check::{Diverges, Expectation, FnCtxt, Needs};
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::{self as hir, ExprKind};
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_infer::traits::Obligation;
@@ -132,7 +132,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &cause,
                 Some(&arm.body),
                 arm_ty,
-                Some(&mut |err: &mut DiagnosticBuilder<'_>| {
+                Some(&mut |err: &mut Diagnostic| {
                     let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
                         Some(ret_coercion) if self.in_tail_expr => {
                             let ret_ty = ret_coercion.borrow().expected_ty();
diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs
index 679cac0b82a..d5187c109e3 100644
--- a/compiler/rustc_typeck/src/check/callee.rs
+++ b/compiler/rustc_typeck/src/check/callee.rs
@@ -2,7 +2,7 @@ use super::method::MethodCallee;
 use super::{Expectation, FnCtxt, TupleArgumentsFlag};
 use crate::type_error_struct;
 
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def::{Namespace, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -277,7 +277,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// likely intention is to call the closure, suggest `(||{})()`. (#55851)
     fn identify_bad_closure_def_and_call(
         &self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         hir_id: hir::HirId,
         callee_node: &hir::ExprKind<'_>,
         callee_span: Span,
@@ -304,7 +304,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// likely intention is to create an array containing tuples.
     fn maybe_suggest_bad_array_definition(
         &self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         call_expr: &'tcx hir::Expr<'tcx>,
         callee_expr: &'tcx hir::Expr<'tcx>,
     ) -> bool {
diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs
index 57076d97246..cd24969bf28 100644
--- a/compiler/rustc_typeck/src/check/cast.rs
+++ b/compiler/rustc_typeck/src/check/cast.rs
@@ -179,7 +179,7 @@ fn make_invalid_casting_error<'a, 'tcx>(
     expr_ty: Ty<'tcx>,
     cast_ty: Ty<'tcx>,
     fcx: &FnCtxt<'a, 'tcx>,
-) -> DiagnosticBuilder<'a> {
+) -> DiagnosticBuilder<'a, ErrorReported> {
     type_error_struct!(
         sess,
         span,
diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index 30ae382de42..7dca95ebdd6 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -37,14 +37,16 @@ pub fn check_wf_new(tcx: TyCtxt<'_>) {
 pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
     match tcx.sess.target.is_abi_supported(abi) {
         Some(true) => (),
-        Some(false) => struct_span_err!(
-            tcx.sess,
-            span,
-            E0570,
-            "`{}` is not a supported ABI for the current target",
-            abi
-        )
-        .emit(),
+        Some(false) => {
+            struct_span_err!(
+                tcx.sess,
+                span,
+                E0570,
+                "`{}` is not a supported ABI for the current target",
+                abi
+            )
+            .emit();
+        }
         None => {
             tcx.struct_span_lint_hir(UNSUPPORTED_CALLING_CONVENTIONS, hir_id, span, |lint| {
                 lint.build("use of calling convention not supported on this target").emit()
@@ -60,7 +62,7 @@ pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Ab
             E0781,
             "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
         )
-        .emit()
+        .emit();
     }
 }
 
diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs
index b011fb8804d..8ca27b010b6 100644
--- a/compiler/rustc_typeck/src/check/coercion.rs
+++ b/compiler/rustc_typeck/src/check/coercion.rs
@@ -37,7 +37,7 @@
 
 use crate::astconv::AstConv;
 use crate::check::FnCtxt;
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
@@ -1307,7 +1307,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         &mut self,
         fcx: &FnCtxt<'a, 'tcx>,
         cause: &ObligationCause<'tcx>,
-        augment_error: &mut dyn FnMut(&mut DiagnosticBuilder<'_>),
+        augment_error: &mut dyn FnMut(&mut Diagnostic),
         label_unit_as_expected: bool,
     ) {
         self.coerce_inner(
@@ -1330,7 +1330,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         cause: &ObligationCause<'tcx>,
         expression: Option<&'tcx hir::Expr<'tcx>>,
         mut expression_ty: Ty<'tcx>,
-        augment_error: Option<&mut dyn FnMut(&mut DiagnosticBuilder<'_>)>,
+        augment_error: Option<&mut dyn FnMut(&mut Diagnostic)>,
         label_expression_as_expected: bool,
     ) {
         // Incorporate whatever type inference information we have
@@ -1520,7 +1520,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
         fcx: &FnCtxt<'a, 'tcx>,
         id: hir::HirId,
         expression: Option<(&'tcx hir::Expr<'tcx>, hir::HirId)>,
-    ) -> DiagnosticBuilder<'a> {
+    ) -> DiagnosticBuilder<'a, ErrorReported> {
         let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err);
 
         let mut pointing_at_return_type = false;
@@ -1603,7 +1603,7 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
 
     fn add_impl_trait_explanation<'a>(
         &self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         cause: &ObligationCause<'tcx>,
         fcx: &FnCtxt<'a, 'tcx>,
         expected: Ty<'tcx>,
diff --git a/compiler/rustc_typeck/src/check/demand.rs b/compiler/rustc_typeck/src/check/demand.rs
index 466f32aa800..80096b90f95 100644
--- a/compiler/rustc_typeck/src/check/demand.rs
+++ b/compiler/rustc_typeck/src/check/demand.rs
@@ -4,7 +4,7 @@ use rustc_trait_selection::infer::InferCtxtExt as _;
 use rustc_trait_selection::traits::ObligationCause;
 
 use rustc_ast::util::parser::PREC_POSTFIX;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::lang_items::LangItem;
 use rustc_hir::{is_range_literal, Node};
@@ -23,7 +23,7 @@ use std::iter;
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub fn emit_coerce_suggestions(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'tcx>,
         expr_ty: Ty<'tcx>,
         expected: Ty<'tcx>,
@@ -57,7 +57,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         self.demand_suptype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -67,7 +67,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         match self.at(cause, self.param_env).sup(expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
@@ -88,7 +88,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         sp: Span,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         self.demand_eqtype_with_origin(&self.misc(sp), expected, actual)
     }
 
@@ -97,7 +97,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         cause: &ObligationCause<'tcx>,
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         match self.at(cause, self.param_env).eq(expected, actual) {
             Ok(InferOk { obligations, value: () }) => {
                 self.register_predicates(obligations);
@@ -134,7 +134,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expected: Ty<'tcx>,
         expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
         allow_two_phase: AllowTwoPhase,
-    ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx>>) {
+    ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorReported>>) {
         let expected = self.resolve_vars_with_obligations(expected);
 
         let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) {
@@ -155,7 +155,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn annotate_expected_due_to_let_ty(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         error: TypeError<'_>,
     ) {
@@ -251,7 +251,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if !lhs.is_syntactic_place_expr() {
                     // We already emitted E0070 "invalid left-hand side of assignment", so we
                     // silence this.
-                    err.delay_as_bug();
+                    err.downgrade_to_delayed_bug();
                 }
             }
             _ => {}
@@ -262,7 +262,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// sole field is of the found type, suggest such variants. (Issue #42764)
     fn suggest_compatible_variants(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         expr_ty: Ty<'tcx>,
@@ -899,7 +899,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn check_for_cast(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         checked_ty: Ty<'tcx>,
         expected_ty: Ty<'tcx>,
@@ -1039,7 +1039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id);
 
         let suggest_fallible_into_or_lhs_from =
-            |err: &mut DiagnosticBuilder<'_>, exp_to_found_is_fallible: bool| {
+            |err: &mut Diagnostic, exp_to_found_is_fallible: bool| {
                 // If we know the expression the expected type is derived from, we might be able
                 // to suggest a widening conversion rather than a narrowing one (which may
                 // panic). For example, given x: u8 and y: u32, if we know the span of "x",
@@ -1083,7 +1083,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
         let suggest_to_change_suffix_or_into =
-            |err: &mut DiagnosticBuilder<'_>,
+            |err: &mut Diagnostic,
              found_to_exp_is_fallible: bool,
              exp_to_found_is_fallible: bool| {
                 let exp_is_lhs =
@@ -1282,11 +1282,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 
     // Report the type inferred by the return statement.
-    fn report_closure_inferred_return_type(
-        &self,
-        err: &mut DiagnosticBuilder<'_>,
-        expected: Ty<'tcx>,
-    ) {
+    fn report_closure_inferred_return_type(&self, err: &mut Diagnostic, expected: Ty<'tcx>) {
         if let Some(sp) = self.ret_coercion_span.get() {
             // If the closure has an explicit return type annotation, or if
             // the closure's return type has been inferred from outside
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index b08ee414fa7..f84036a7a39 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -25,6 +25,7 @@ use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructEx
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stack::ensure_sufficient_stack;
+use rustc_errors::Diagnostic;
 use rustc_errors::ErrorReported;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
 use rustc_hir as hir;
@@ -60,7 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
-        extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
+        extend_err: impl Fn(&mut Diagnostic),
     ) -> Ty<'tcx> {
         self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err)
     }
@@ -69,7 +70,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Expectation<'tcx>,
-        extend_err: impl Fn(&mut DiagnosticBuilder<'_>),
+        extend_err: impl Fn(&mut Diagnostic),
     ) -> Ty<'tcx> {
         let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool);
         let mut ty = self.check_expr_with_expectation(expr, expected);
@@ -485,7 +486,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
         });
         if !is_named {
-            self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span })
+            self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span });
         }
     }
 
@@ -1469,14 +1470,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                                     self.register_predicates(obligations)
                                                 }
                                                 // FIXME: Need better diagnostics for `FieldMisMatch` error
-                                                Err(_) => self
-                                                    .report_mismatched_types(
+                                                Err(_) => {
+                                                    self.report_mismatched_types(
                                                         &cause,
                                                         target_ty,
                                                         fru_ty,
                                                         FieldMisMatch(variant.name, ident.name),
                                                     )
-                                                    .emit(),
+                                                    .emit();
+                                                }
                                             }
                                         }
                                         fru_ty
@@ -1484,22 +1486,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                     .collect()
                             }
                             _ => {
-                                return self
-                                    .report_mismatched_types(
-                                        &self.misc(base_expr.span),
-                                        adt_ty,
-                                        base_ty,
-                                        Sorts(ExpectedFound::new(true, adt_ty, base_ty)),
-                                    )
-                                    .emit();
+                                self.report_mismatched_types(
+                                    &self.misc(base_expr.span),
+                                    adt_ty,
+                                    base_ty,
+                                    Sorts(ExpectedFound::new(true, adt_ty, base_ty)),
+                                )
+                                .emit();
+                                return;
                             }
                         }
                     }
                     _ => {
-                        return self
-                            .tcx
+                        self.tcx
                             .sess
                             .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+                        return;
                     }
                 }
             } else {
@@ -1528,10 +1530,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         })
                         .collect(),
                     _ => {
-                        return self
-                            .tcx
+                        self.tcx
                             .sess
                             .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span });
+                        return;
                     }
                 }
             };
@@ -1923,7 +1925,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_await_on_field_access(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         field_ident: Ident,
         base: &'tcx hir::Expr<'tcx>,
         ty: Ty<'tcx>,
@@ -2123,7 +2125,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err.emit();
     }
 
-    fn point_at_param_definition(&self, err: &mut DiagnosticBuilder<'_>, param: ty::ParamTy) {
+    fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) {
         let generics = self.tcx.generics_of(self.body_id.owner.to_def_id());
         let generic_param = generics.type_param(&param, self.tcx);
         if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind {
@@ -2142,7 +2144,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_fields_on_recordish(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         def: &'tcx ty::AdtDef,
         field: Ident,
         access_span: Span,
@@ -2171,7 +2173,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn maybe_suggest_array_indexing(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         base: &hir::Expr<'_>,
         field: Ident,
@@ -2195,7 +2197,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_first_deref_field(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         base: &hir::Expr<'_>,
         field: Ident,
@@ -2212,7 +2214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         field: Ident,
         expr_t: Ty<'tcx>,
         id: HirId,
-    ) -> DiagnosticBuilder<'_> {
+    ) -> DiagnosticBuilder<'_, ErrorReported> {
         let span = field.span;
         debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
 
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index f6cd1369403..0fe5e74da89 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -8,7 +8,7 @@ use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy};
 
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, DiagnosticBuilder, ErrorReported};
+use rustc_errors::{Applicability, Diagnostic, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -953,7 +953,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn note_internal_mutation_in_method(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -998,7 +998,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn note_need_for_fn_pointer(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index d3d42a1f37c..4b6460b62b7 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -11,7 +11,7 @@ use crate::check::{
 
 use rustc_ast as ast;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{Applicability, Diagnostic, DiagnosticId};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -460,7 +460,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             fn variadic_error<'tcx>(sess: &Session, span: Span, ty: Ty<'tcx>, cast_ty: &str) {
                 use crate::structured_errors::MissingCastForVariadicArg;
 
-                MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit()
+                MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
             }
 
             for arg in provided_args.iter().skip(expected_arg_count) {
@@ -837,7 +837,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                                             kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
                                             ..
                                         })) => {
-                                            err.delay_as_bug();
+                                            err.downgrade_to_delayed_bug();
                                         }
                                         _ => {}
                                     }
@@ -890,7 +890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         blk: &'tcx hir::Block<'tcx>,
         expected_ty: Ty<'tcx>,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
     ) {
         if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) {
             if let StatementAsExpression::NeedsBoxing = boxed {
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
index f6a2447572f..9e4b88e3746 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
@@ -4,7 +4,7 @@ use crate::astconv::AstConv;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_span::{self, MultiSpan, Span};
 
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind};
 use rustc_hir::lang_items::LangItem;
@@ -22,11 +22,7 @@ use rustc_middle::ty::subst::GenericArgKind;
 use std::iter;
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-    pub(in super::super) fn suggest_semicolon_at_end(
-        &self,
-        span: Span,
-        err: &mut DiagnosticBuilder<'_>,
-    ) {
+    pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) {
         err.span_suggestion_short(
             span.shrink_to_hi(),
             "consider using a semicolon here",
@@ -42,7 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// - Possible missing return type if the return type is the default, and not `fn main()`.
     pub fn suggest_mismatched_types_on_tail(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -81,7 +77,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     fn suggest_fn_call(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -211,7 +207,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub fn suggest_deref_ref_or_into(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'tcx>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -312,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// in the heap by calling `Box::new()`.
     pub(in super::super) fn suggest_boxing_when_appropriate(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -347,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// suggest a non-capturing closure
     pub(in super::super) fn suggest_no_capture_closure(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
     ) {
@@ -382,7 +378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(skip(self, err))]
     pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -477,7 +473,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// it suggests adding a semicolon.
     fn suggest_missing_semicolon(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expression: &'tcx hir::Expr<'tcx>,
         expected: Ty<'tcx>,
     ) {
@@ -518,7 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// type.
     pub(in super::super) fn suggest_missing_return_type(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         fn_decl: &hir::FnDecl<'_>,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
@@ -580,7 +576,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// ```
     fn try_suggest_return_impl_trait(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expected: Ty<'tcx>,
         found: Ty<'tcx>,
         fn_id: hir::HirId,
@@ -681,7 +677,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn suggest_missing_break_or_return_expr(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &'tcx hir::Expr<'tcx>,
         fn_decl: &hir::FnDecl<'_>,
         expected: Ty<'tcx>,
@@ -751,7 +747,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     pub(in super::super) fn suggest_missing_parentheses(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
     ) {
         let sp = self.tcx.sess.source_map().start_point(expr.span);
diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs
index 780a57278c5..999aafbd537 100644
--- a/compiler/rustc_typeck/src/check/method/mod.rs
+++ b/compiler/rustc_typeck/src/check/method/mod.rs
@@ -14,7 +14,7 @@ pub use self::MethodError::*;
 use crate::check::FnCtxt;
 use crate::ObligationCause;
 use rustc_data_structures::sync::Lrc;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Namespace};
 use rustc_hir::def_id::DefId;
@@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     #[instrument(level = "debug", skip(self, err, call_expr))]
     crate fn suggest_method_call(
         &self,
-        err: &mut DiagnosticBuilder<'a>,
+        err: &mut Diagnostic,
         msg: &str,
         method_name: Ident,
         self_ty: Ty<'tcx>,
diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs
index 3213148f6a3..ccaea10233d 100644
--- a/compiler/rustc_typeck/src/check/method/suggest.rs
+++ b/compiler/rustc_typeck/src/check/method/suggest.rs
@@ -3,7 +3,9 @@
 
 use crate::check::FnCtxt;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::lang_items::LangItem;
@@ -91,14 +93,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         source: SelfSource<'tcx>,
         error: MethodError<'tcx>,
         args: Option<&'tcx [hir::Expr<'tcx>]>,
-    ) -> Option<DiagnosticBuilder<'_>> {
+    ) -> Option<DiagnosticBuilder<'_, ErrorReported>> {
         // Avoid suggestions when we don't know what's going on.
         if rcvr_ty.references_error() {
             return None;
         }
 
         let report_candidates = |span: Span,
-                                 err: &mut DiagnosticBuilder<'_>,
+                                 err: &mut Diagnostic,
                                  mut sources: Vec<CandidateSource>,
                                  sugg_span: Span| {
             sources.sort();
@@ -268,7 +270,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         (None, true) => "variant",
                     }
                 };
-                let mut err = if !actual.references_error() {
+                // FIXME(eddyb) this intendation is probably unnecessary.
+                let mut err = {
                     // Suggest clamping down the type if the method that is being attempted to
                     // be used exists at all, and the type is an ambiguous numeric type
                     // ({integer}/{float}).
@@ -461,10 +464,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                         err
                     }
-                } else {
-                    tcx.sess.diagnostic().struct_dummy()
                 };
 
+                if actual.references_error() {
+                    err.downgrade_to_delayed_bug();
+                }
+
                 if let Some(def) = actual.ty_adt_def() {
                     if let Some(full_sp) = tcx.hir().span_if_local(def.did) {
                         let def_sp = tcx.sess.source_map().guess_head_span(full_sp);
@@ -625,10 +630,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 }
 
                 if self.is_fn_ty(rcvr_ty, span) {
-                    fn report_function<T: std::fmt::Display>(
-                        err: &mut DiagnosticBuilder<'_>,
-                        name: T,
-                    ) {
+                    fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
                         err.note(
                             &format!("`{}` is a function, perhaps you wish to call it", name,),
                         );
@@ -1111,7 +1113,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     crate fn note_unmet_impls_on_type(
         &self,
-        err: &mut rustc_errors::DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         errors: Vec<FulfillmentError<'tcx>>,
     ) {
         let all_local_types_needing_impls =
@@ -1187,7 +1189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_derive(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         unsatisfied_predicates: &[(
             ty::Predicate<'tcx>,
             Option<ty::Predicate<'tcx>>,
@@ -1287,7 +1289,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_await_before_method(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         item_name: Ident,
         ty: Ty<'tcx>,
         call: &hir::Expr<'_>,
@@ -1311,7 +1313,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_use_candidates(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         mut msg: String,
         candidates: Vec<DefId>,
     ) {
@@ -1416,7 +1418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_valid_traits(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         valid_out_of_scope_traits: Vec<DefId>,
     ) -> bool {
         if !valid_out_of_scope_traits.is_empty() {
@@ -1454,7 +1456,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn suggest_traits_to_import(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         rcvr_ty: Ty<'tcx>,
         item_name: Ident,
@@ -1983,7 +1985,7 @@ fn find_use_placement<'tcx>(tcx: TyCtxt<'tcx>, target_module: LocalDefId) -> (Op
 fn print_disambiguation_help<'tcx>(
     item_name: Ident,
     args: Option<&'tcx [hir::Expr<'tcx>]>,
-    err: &mut DiagnosticBuilder<'_>,
+    err: &mut Diagnostic,
     trait_name: String,
     rcvr_ty: Ty<'_>,
     kind: ty::AssocKind,
diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs
index 6e0b902a00b..e2a91635a2d 100644
--- a/compiler/rustc_typeck/src/check/mod.rs
+++ b/compiler/rustc_typeck/src/check/mod.rs
@@ -139,11 +139,13 @@ pub use self::Expectation::*;
 #[macro_export]
 macro_rules! type_error_struct {
     ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
+        let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*);
+
         if $typ.references_error() {
-            $session.diagnostic().struct_dummy()
-        } else {
-            rustc_errors::struct_span_err!($session, $span, $code, $($message)*)
+            err.downgrade_to_delayed_bug();
         }
+
+        err
     })
 }
 
diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs
index dd49d6f4892..5873b0f52a6 100644
--- a/compiler/rustc_typeck/src/check/op.rs
+++ b/compiler/rustc_typeck/src/check/op.rs
@@ -3,7 +3,7 @@
 use super::method::MethodCallee;
 use super::{has_expected_num_generic_args, FnCtxt};
 use rustc_ast as ast;
-use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
 use rustc_hir as hir;
 use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
 use rustc_middle::ty::adjustment::{
@@ -483,7 +483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
     fn add_type_neq_err_label(
         &self,
-        err: &mut rustc_errors::DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         span: Span,
         ty: Ty<'tcx>,
         other_ty: Ty<'tcx>,
@@ -545,7 +545,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         rhs_expr: &'tcx hir::Expr<'tcx>,
         lhs_ty: Ty<'tcx>,
         rhs_ty: Ty<'tcx>,
-        err: &mut rustc_errors::DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         is_assign: IsAssign,
         op: hir::BinOp,
     ) -> bool {
@@ -937,7 +937,7 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool
 fn suggest_constraining_param(
     tcx: TyCtxt<'_>,
     body_id: hir::HirId,
-    mut err: &mut DiagnosticBuilder<'_>,
+    mut err: &mut Diagnostic,
     lhs_ty: Ty<'_>,
     rhs_ty: Ty<'_>,
     missing_trait: &str,
diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs
index 77ab1d1de42..7c6917734b2 100644
--- a/compiler/rustc_typeck/src/check/pat.rs
+++ b/compiler/rustc_typeck/src/check/pat.rs
@@ -2,7 +2,9 @@ use crate::check::FnCtxt;
 use rustc_ast as ast;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
@@ -98,7 +100,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
         expected: Ty<'tcx>,
         actual: Ty<'tcx>,
         ti: TopInfo<'tcx>,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)
     }
 
@@ -512,7 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ty
     }
 
-    fn endpoint_has_type(&self, err: &mut DiagnosticBuilder<'_>, span: Span, ty: Ty<'_>) {
+    fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) {
         if !ty.references_error() {
             err.span_label(span, &format!("this is of type `{}`", ty));
         }
@@ -645,7 +647,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn borrow_pat_suggestion(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diagnostic,
         pat: &Pat<'_>,
         inner: &Pat<'_>,
         expected: Ty<'tcx>,
@@ -783,7 +785,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn maybe_suggest_range_literal(
         &self,
-        e: &mut DiagnosticBuilder<'_>,
+        e: &mut Diagnostic,
         opt_def_id: Option<hir::def_id::DefId>,
         ident: Ident,
     ) -> bool {
@@ -817,7 +819,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn emit_bad_pat_path<'b>(
         &self,
-        mut e: DiagnosticBuilder<'_>,
+        mut e: DiagnosticBuilder<'_, ErrorReported>,
         pat_span: Span,
         res: Res,
         pat_res: Res,
@@ -1368,7 +1370,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         variant: &VariantDef,
         pat: &'_ Pat<'_>,
         fields: &[hir::PatField<'_>],
-    ) -> Option<DiagnosticBuilder<'_>> {
+    ) -> Option<DiagnosticBuilder<'_, ErrorReported>> {
         // if this is a tuple struct, then all field names will be numbers
         // so if any fields in a struct pattern use shorthand syntax, they will
         // be invalid identifiers (for example, Foo { 0, 1 }).
@@ -1441,7 +1443,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         inexistent_fields: &[Ident],
         unmentioned_fields: &mut Vec<(&ty::FieldDef, Ident)>,
         variant: &ty::VariantDef,
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let tcx = self.tcx;
         let (field_names, t, plural) = if inexistent_fields.len() == 1 {
             (format!("a field named `{}`", inexistent_fields[0]), "this", "")
@@ -1537,7 +1539,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
         variant: &ty::VariantDef,
-    ) -> Option<DiagnosticBuilder<'tcx>> {
+    ) -> Option<DiagnosticBuilder<'tcx, ErrorReported>> {
         if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) {
             let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
                 s.print_qpath(qpath, false)
@@ -1619,7 +1621,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         &self,
         pat: &Pat<'_>,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = self
             .tcx
             .sess
@@ -1711,7 +1713,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         unmentioned_fields: &[(&ty::FieldDef, Ident)],
         have_inaccessible_fields: bool,
         fields: &'tcx [hir::PatField<'tcx>],
-    ) -> DiagnosticBuilder<'tcx> {
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" };
         let field_names = if unmentioned_fields.len() == 1 {
             format!("field `{}`{}", unmentioned_fields[0].1, inaccessible)
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 55757251e26..4ab654560ea 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -4,7 +4,7 @@ use crate::constrained_generic_params::{identify_constrained_generic_params, Par
 
 use rustc_ast as ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit as hir_visit;
@@ -448,7 +448,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
                  for more information",
             );
 
-            err.emit()
+            err.emit();
         }
     }
 }
@@ -843,7 +843,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                             "using {} as const generic parameters is forbidden",
                             unsupported_type
                         ),
-                    )
+                    );
                 } else {
                     let mut err = tcx.sess.struct_span_err(
                         hir_ty.span,
@@ -858,7 +858,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
                             "more complex types are supported with `#![feature(adt_const_params)]`",
                         );
                     }
-                    err.emit()
+                    err.emit();
                 }
             };
 
@@ -1729,12 +1729,14 @@ fn check_variances_for_type_defn<'tcx>(
 
         match param.name {
             hir::ParamName::Error => {}
-            _ => report_bivariance(tcx, param),
+            _ => {
+                report_bivariance(tcx, param);
+            }
         }
     }
 }
 
-fn report_bivariance(tcx: TyCtxt<'_>, param: &rustc_hir::GenericParam<'_>) {
+fn report_bivariance(tcx: TyCtxt<'_>, param: &rustc_hir::GenericParam<'_>) -> ErrorReported {
     let span = param.span;
     let param_name = param.name.ident().name;
     let mut err = error_392(tcx, span, param_name);
@@ -1943,7 +1945,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     }
 }
 
-fn error_392(tcx: TyCtxt<'_>, span: Span, param_name: Symbol) -> DiagnosticBuilder<'_> {
+fn error_392(
+    tcx: TyCtxt<'_>,
+    span: Span,
+    param_name: Symbol,
+) -> DiagnosticBuilder<'_, ErrorReported> {
     let mut err =
         struct_span_err!(tcx.sess, span, E0392, "parameter `{}` is never used", param_name);
     err.span_label(span, "unused parameter");
diff --git a/compiler/rustc_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs
index 401ba188728..ac1f2db848f 100644
--- a/compiler/rustc_typeck/src/coherence/builtin.rs
+++ b/compiler/rustc_typeck/src/coherence/builtin.rs
@@ -74,7 +74,8 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
 
     debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
 
-    match can_type_implement_copy(tcx, param_env, self_type) {
+    let cause = traits::ObligationCause::misc(span, impl_hir_id);
+    match can_type_implement_copy(tcx, param_env, self_type, cause) {
         Ok(()) => {}
         Err(CopyImplementationError::InfrigingFields(fields)) => {
             let item = tcx.hir().expect_item(impl_did);
@@ -93,7 +94,7 @@ fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
             for span in fields.iter().map(|f| tcx.def_span(f.did)) {
                 err.span_label(span, "this field does not implement `Copy`");
             }
-            err.emit()
+            err.emit();
         }
         Err(CopyImplementationError::NotAnAdt) => {
             let item = tcx.hir().expect_item(impl_did);
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 54fffeb3cda..6c9059cfdd4 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -159,7 +159,7 @@ fn emit_orphan_check_error<'tcx>(
     generics: &hir::Generics<'tcx>,
     err: traits::OrphanCheckErr<'tcx>,
 ) -> Result<!, ErrorReported> {
-    match err {
+    Err(match err {
         traits::OrphanCheckErr::NonLocalInputType(tys) => {
             let mut err = struct_span_err!(
                 tcx.sess,
@@ -269,9 +269,7 @@ fn emit_orphan_check_error<'tcx>(
                 .emit(),
             }
         }
-    }
-
-    Err(ErrorReported)
+    })
 }
 
 #[derive(Default)]
@@ -439,6 +437,7 @@ fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty:
             }
 
             match t.kind() {
+                ty::Adt(def, substs) if def.is_phantom_data() => substs.super_visit_with(self),
                 ty::Adt(def, substs) => {
                     // @lcnr: This is the only place where cycles can happen. We avoid this
                     // by only visiting each `DefId` once.
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 392144ca763..cf2164763b1 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -26,7 +26,7 @@ use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
 use rustc_data_structures::captures::Captures;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
-use rustc_errors::{struct_span_err, Applicability};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
@@ -321,7 +321,7 @@ fn bad_placeholder<'tcx>(
     tcx: TyCtxt<'tcx>,
     mut spans: Vec<Span>,
     kind: &'static str,
-) -> rustc_errors::DiagnosticBuilder<'tcx> {
+) -> DiagnosticBuilder<'tcx, ErrorReported> {
     let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) };
 
     spans.sort();
@@ -730,7 +730,7 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
         // These don't define types.
         hir::ItemKind::ExternCrate(_)
         | hir::ItemKind::Use(..)
-        | hir::ItemKind::Macro(_)
+        | hir::ItemKind::Macro(..)
         | hir::ItemKind::Mod(_)
         | hir::ItemKind::GlobalAsm(_) => {}
         hir::ItemKind::ForeignMod { items, .. } => {
@@ -1277,19 +1277,21 @@ fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
 
                         return None;
                     }
-                    Some(item) => tcx
-                        .sess
-                        .struct_span_err(item.span, "Not a function")
-                        .span_note(attr_span, "required by this annotation")
-                        .note(
-                            "All `#[rustc_must_implement_one_of]` arguments \
+                    Some(item) => {
+                        tcx.sess
+                            .struct_span_err(item.span, "Not a function")
+                            .span_note(attr_span, "required by this annotation")
+                            .note(
+                                "All `#[rustc_must_implement_one_of]` arguments \
                             must be associated function names",
-                        )
-                        .emit(),
-                    None => tcx
-                        .sess
-                        .struct_span_err(ident.span, "Function not found in this trait")
-                        .emit(),
+                            )
+                            .emit();
+                    }
+                    None => {
+                        tcx.sess
+                            .struct_span_err(ident.span, "Function not found in this trait")
+                            .emit();
+                    }
                 }
 
                 Some(())
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
index 78097e3697f..6cef3e9d940 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
@@ -393,13 +393,14 @@ fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tc
                             tcx.def_path_str(trait_ref.def_id),
                         ),
                     )
-                    .emit()
+                    .emit();
             }
         }
-        _ => tcx
-            .sess
-            .struct_span_err(span, &format!("cannot specialize on `{:?}`", predicate))
-            .emit(),
+        _ => {
+            tcx.sess
+                .struct_span_err(span, &format!("cannot specialize on `{:?}`", predicate))
+                .emit();
+        }
     }
 }
 
diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_typeck/src/structured_errors.rs
index 04d04304e70..8621375fc63 100644
--- a/compiler/rustc_typeck/src/structured_errors.rs
+++ b/compiler/rustc_typeck/src/structured_errors.rs
@@ -6,7 +6,7 @@ pub use self::{
     missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*,
 };
 
-use rustc_errors::{DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_session::Session;
 
 pub trait StructuredDiagnostic<'tcx> {
@@ -14,7 +14,7 @@ pub trait StructuredDiagnostic<'tcx> {
 
     fn code(&self) -> DiagnosticId;
 
-    fn diagnostic(&self) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic(&self) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let err = self.diagnostic_common();
 
         if self.session().teach(&self.code()) {
@@ -24,13 +24,19 @@ pub trait StructuredDiagnostic<'tcx> {
         }
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx>;
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorReported>;
 
-    fn diagnostic_regular(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_regular(
+        &self,
+        err: DiagnosticBuilder<'tcx, ErrorReported>,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         err
     }
 
-    fn diagnostic_extended(&self, err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_extended(
+        &self,
+        err: DiagnosticBuilder<'tcx, ErrorReported>,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         err
     }
 }
diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs
index 674b0e463f5..2dc9f26004c 100644
--- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs
@@ -1,5 +1,5 @@
 use crate::structured_errors::StructuredDiagnostic;
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_middle::ty::{Ty, TypeFoldable};
 use rustc_session::Session;
 use rustc_span::Span;
@@ -20,16 +20,16 @@ impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx> {
         rustc_errors::error_code!(E0617)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
-        let mut err = if self.ty.references_error() {
-            self.sess.diagnostic().struct_dummy()
-        } else {
-            self.sess.struct_span_fatal_with_code(
-                self.span,
-                &format!("can't pass `{}` to variadic function", self.ty),
-                self.code(),
-            )
-        };
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorReported> {
+        let mut err = self.sess.struct_span_fatal_with_code(
+            self.span,
+            &format!("can't pass `{}` to variadic function", self.ty),
+            self.code(),
+        );
+
+        if self.ty.references_error() {
+            err.downgrade_to_delayed_bug();
+        }
 
         if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) {
             err.span_suggestion(
@@ -45,7 +45,10 @@ impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx> {
         err
     }
 
-    fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_extended(
+        &self,
+        mut err: DiagnosticBuilder<'tcx, ErrorReported>,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         err.note(&format!(
             "certain types, like `{}`, must be casted before passing them to a \
                 variadic function, because of arcane ABI rules dictated by the C \
diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs
index d0477a3e748..8d2cdbb170b 100644
--- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs
+++ b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs
@@ -1,5 +1,5 @@
 use crate::structured_errors::StructuredDiagnostic;
-use rustc_errors::{DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_middle::ty::{Ty, TypeFoldable};
 use rustc_session::Session;
 use rustc_span::Span;
@@ -20,22 +20,27 @@ impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> {
         rustc_errors::error_code!(E0607)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorReported> {
+        let mut err = self.sess.struct_span_fatal_with_code(
+            self.span,
+            &format!(
+                "cannot cast thin pointer `{}` to fat pointer `{}`",
+                self.expr_ty, self.cast_ty
+            ),
+            self.code(),
+        );
+
         if self.expr_ty.references_error() {
-            self.sess.diagnostic().struct_dummy()
-        } else {
-            self.sess.struct_span_fatal_with_code(
-                self.span,
-                &format!(
-                    "cannot cast thin pointer `{}` to fat pointer `{}`",
-                    self.expr_ty, self.cast_ty
-                ),
-                self.code(),
-            )
+            err.downgrade_to_delayed_bug();
         }
+
+        err
     }
 
-    fn diagnostic_extended(&self, mut err: DiagnosticBuilder<'tcx>) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_extended(
+        &self,
+        mut err: DiagnosticBuilder<'tcx, ErrorReported>,
+    ) -> DiagnosticBuilder<'tcx, ErrorReported> {
         err.help(
             "Thin pointers are \"simple\" pointers: they are purely a reference to a
 memory address.
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
index a1c29457709..b763b51dd01 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
@@ -1,5 +1,7 @@
 use crate::structured_errors::StructuredDiagnostic;
-use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId};
+use rustc_errors::{
+    pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorReported,
+};
 use rustc_hir as hir;
 use rustc_middle::hir::map::fn_sig;
 use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath;
@@ -362,7 +364,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> {
+    fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let span = self.path_segment.ident.span;
         let msg = self.create_error_message();
 
@@ -370,7 +372,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Builds the `expected 1 type argument / supplied 2 type arguments` message.
-    fn notify(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn notify(&self, err: &mut Diagnostic) {
         let (quantifier, bound) = self.get_quantifier_and_bound();
         let provided_args = self.num_provided_args();
 
@@ -422,7 +424,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest(&self, err: &mut Diagnostic) {
         debug!(
             "suggest(self.provided {:?}, self.gen_args.span(): {:?})",
             self.num_provided_args(),
@@ -449,7 +451,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String>;
     /// ```
-    fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_adding_args(&self, err: &mut Diagnostic) {
         if self.gen_args.parenthesized {
             return;
         }
@@ -465,7 +467,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) {
         debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment);
         let num_missing_args = self.num_missing_lifetime_args();
         let num_params_to_take = num_missing_args;
@@ -547,7 +549,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
         }
     }
 
-    fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) {
         let num_missing_args = self.num_missing_type_or_const_args();
         let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args));
 
@@ -602,7 +604,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     /// ```text
     /// type Map = HashMap<String, String, String, String>;
     /// ```
-    fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) {
         let num_provided_lt_args = self.num_provided_lifetime_args();
         let num_provided_type_const_args = self.num_provided_type_or_const_args();
         let num_provided_args = num_provided_lt_args + num_provided_type_const_args;
@@ -617,7 +619,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
 
         let remove_entire_generics = num_redundant_args >= self.gen_args.args.len();
 
-        let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| {
+        let remove_lifetime_args = |err: &mut Diagnostic| {
             let mut lt_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -659,7 +661,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
             );
         };
 
-        let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| {
+        let remove_type_or_const_args = |err: &mut Diagnostic| {
             let mut gen_arg_spans = Vec::new();
             let mut found_redundant = false;
             for arg in self.gen_args.args {
@@ -729,7 +731,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
     }
 
     /// Builds the `type defined here` message.
-    fn show_definition(&self, err: &mut DiagnosticBuilder<'_>) {
+    fn show_definition(&self, err: &mut Diagnostic) {
         let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) {
             if self.tcx.sess.source_map().span_to_snippet(def_span).is_ok() {
                 def_span.into()
@@ -789,7 +791,7 @@ impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> {
         rustc_errors::error_code!(E0107)
     }
 
-    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx> {
+    fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorReported> {
         let mut err = self.start_diagnostics();
 
         self.notify(&mut err);
diff --git a/library/core/benches/slice.rs b/library/core/benches/slice.rs
index 04efa520787..9b86a0ca97c 100644
--- a/library/core/benches/slice.rs
+++ b/library/core/benches/slice.rs
@@ -89,6 +89,15 @@ fn binary_search_l3_worst_case(b: &mut Bencher) {
     binary_search_worst_case(b, Cache::L3);
 }
 
+#[derive(Clone)]
+struct Rgb(u8, u8, u8);
+
+impl Rgb {
+    fn gen(i: usize) -> Self {
+        Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42))
+    }
+}
+
 macro_rules! rotate {
     ($fn:ident, $n:expr, $mapper:expr) => {
         #[bench]
@@ -104,17 +113,43 @@ macro_rules! rotate {
     };
 }
 
-#[derive(Clone)]
-struct Rgb(u8, u8, u8);
-
 rotate!(rotate_u8, 32, |i| i as u8);
-rotate!(rotate_rgb, 32, |i| Rgb(i as u8, (i as u8).wrapping_add(7), (i as u8).wrapping_add(42)));
+rotate!(rotate_rgb, 32, Rgb::gen);
 rotate!(rotate_usize, 32, |i| i);
 rotate!(rotate_16_usize_4, 16, |i| [i; 4]);
 rotate!(rotate_16_usize_5, 16, |i| [i; 5]);
 rotate!(rotate_64_usize_4, 64, |i| [i; 4]);
 rotate!(rotate_64_usize_5, 64, |i| [i; 5]);
 
+macro_rules! swap_with_slice {
+    ($fn:ident, $n:expr, $mapper:expr) => {
+        #[bench]
+        fn $fn(b: &mut Bencher) {
+            let mut x = (0usize..$n).map(&$mapper).collect::<Vec<_>>();
+            let mut y = ($n..($n * 2)).map(&$mapper).collect::<Vec<_>>();
+            let mut skip = 0;
+            b.iter(|| {
+                for _ in 0..32 {
+                    x[skip..].swap_with_slice(&mut y[..($n - skip)]);
+                    skip = black_box(skip + 1) % 8;
+                }
+                black_box((x[$n / 3].clone(), y[$n * 2 / 3].clone()))
+            })
+        }
+    };
+}
+
+swap_with_slice!(swap_with_slice_u8_30, 30, |i| i as u8);
+swap_with_slice!(swap_with_slice_u8_3000, 3000, |i| i as u8);
+swap_with_slice!(swap_with_slice_rgb_30, 30, Rgb::gen);
+swap_with_slice!(swap_with_slice_rgb_3000, 3000, Rgb::gen);
+swap_with_slice!(swap_with_slice_usize_30, 30, |i| i);
+swap_with_slice!(swap_with_slice_usize_3000, 3000, |i| i);
+swap_with_slice!(swap_with_slice_4x_usize_30, 30, |i| [i; 4]);
+swap_with_slice!(swap_with_slice_4x_usize_3000, 3000, |i| [i; 4]);
+swap_with_slice!(swap_with_slice_5x_usize_30, 30, |i| [i; 5]);
+swap_with_slice!(swap_with_slice_5x_usize_3000, 3000, |i| [i; 5]);
+
 #[bench]
 fn fill_byte_sized(b: &mut Bencher) {
     #[derive(Copy, Clone)]
diff --git a/library/core/src/mem/mod.rs b/library/core/src/mem/mod.rs
index 989ec0639cd..b5c1ae37e5e 100644
--- a/library/core/src/mem/mod.rs
+++ b/library/core/src/mem/mod.rs
@@ -700,10 +700,49 @@ pub unsafe fn uninitialized<T>() -> T {
 #[stable(feature = "rust1", since = "1.0.0")]
 #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
 pub const fn swap<T>(x: &mut T, y: &mut T) {
-    // SAFETY: the raw pointers have been created from safe mutable references satisfying all the
-    // constraints on `ptr::swap_nonoverlapping_one`
+    // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary
+    // reinterpretation of values as (chunkable) byte arrays, and the loop in the
+    // block optimization in `swap_slice` is hard to rewrite back
+    // into the (unoptimized) direct swapping implementation, so we disable it.
+    // FIXME(eddyb) the block optimization also prevents MIR optimizations from
+    // understanding `mem::replace`, `Option::take`, etc. - a better overall
+    // solution might be to make `ptr::swap_nonoverlapping` into an intrinsic, which
+    // a backend can choose to implement using the block optimization, or not.
+    #[cfg(not(target_arch = "spirv"))]
+    {
+        // For types that are larger multiples of their alignment, the simple way
+        // tends to copy the whole thing to stack rather than doing it one part
+        // at a time, so instead treat them as one-element slices and piggy-back
+        // the slice optimizations that will split up the swaps.
+        if size_of::<T>() / align_of::<T>() > 4 {
+            // SAFETY: exclusive references always point to one non-overlapping
+            // element and are non-null and properly aligned.
+            return unsafe { ptr::swap_nonoverlapping(x, y, 1) };
+        }
+    }
+
+    // If a scalar consists of just a small number of alignment units, let
+    // the codegen just swap those pieces directly, as it's likely just a
+    // few instructions and anything else is probably overcomplicated.
+    //
+    // Most importantly, this covers primitives and simd types that tend to
+    // have size=align where doing anything else can be a pessimization.
+    // (This will also be used for ZSTs, though any solution works for them.)
+    swap_simple(x, y);
+}
+
+/// Same as [`swap`] semantically, but always uses the simple implementation.
+///
+/// Used elsewhere in `mem` and `ptr` at the bottom layer of calls.
+#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
+#[inline]
+pub(crate) const fn swap_simple<T>(x: &mut T, y: &mut T) {
+    // SAFETY: exclusive references are always valid to read/write,
+    // are non-overlapping, and nothing here panics so it's drop-safe.
     unsafe {
-        ptr::swap_nonoverlapping_one(x, y);
+        let z = ptr::read(x);
+        ptr::copy_nonoverlapping(y, x, 1);
+        ptr::write(y, z);
     }
 }
 
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 8ab72e6aeea..ff71fadb614 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -419,106 +419,58 @@ pub const unsafe fn swap<T>(x: *mut T, y: *mut T) {
 #[stable(feature = "swap_nonoverlapping", since = "1.27.0")]
 #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
 pub const unsafe fn swap_nonoverlapping<T>(x: *mut T, y: *mut T, count: usize) {
-    let x = x as *mut u8;
-    let y = y as *mut u8;
-    let len = mem::size_of::<T>() * count;
-    // SAFETY: the caller must guarantee that `x` and `y` are
-    // valid for writes and properly aligned.
-    unsafe { swap_nonoverlapping_bytes(x, y, len) }
-}
+    macro_rules! attempt_swap_as_chunks {
+        ($ChunkTy:ty) => {
+            if mem::align_of::<T>() >= mem::align_of::<$ChunkTy>()
+                && mem::size_of::<T>() % mem::size_of::<$ChunkTy>() == 0
+            {
+                let x: *mut MaybeUninit<$ChunkTy> = x.cast();
+                let y: *mut MaybeUninit<$ChunkTy> = y.cast();
+                let count = count * (mem::size_of::<T>() / mem::size_of::<$ChunkTy>());
+                // SAFETY: these are the same bytes that the caller promised were
+                // ok, just typed as `MaybeUninit<ChunkTy>`s instead of as `T`s.
+                // The `if` condition above ensures that we're not violating
+                // alignment requirements, and that the division is exact so
+                // that we don't lose any bytes off the end.
+                return unsafe { swap_nonoverlapping_simple(x, y, count) };
+            }
+        };
+    }
 
-#[inline]
-#[rustc_const_unstable(feature = "const_swap", issue = "83163")]
-pub(crate) const unsafe fn swap_nonoverlapping_one<T>(x: *mut T, y: *mut T) {
-    // NOTE(eddyb) SPIR-V's Logical addressing model doesn't allow for arbitrary
-    // reinterpretation of values as (chunkable) byte arrays, and the loop in the
-    // block optimization in `swap_nonoverlapping_bytes` is hard to rewrite back
-    // into the (unoptimized) direct swapping implementation, so we disable it.
-    // FIXME(eddyb) the block optimization also prevents MIR optimizations from
-    // understanding `mem::replace`, `Option::take`, etc. - a better overall
-    // solution might be to make `swap_nonoverlapping` into an intrinsic, which
-    // a backend can choose to implement using the block optimization, or not.
-    #[cfg(not(target_arch = "spirv"))]
+    // Split up the slice into small power-of-two-sized chunks that LLVM is able
+    // to vectorize (unless it's a special type with more-than-pointer alignment,
+    // because we don't want to pessimize things like slices of SIMD vectors.)
+    if mem::align_of::<T>() <= mem::size_of::<usize>()
+        && (!mem::size_of::<T>().is_power_of_two()
+            || mem::size_of::<T>() > mem::size_of::<usize>() * 2)
     {
-        // Only apply the block optimization in `swap_nonoverlapping_bytes` for types
-        // at least as large as the block size, to avoid pessimizing codegen.
-        if mem::size_of::<T>() >= 32 {
-            // SAFETY: the caller must uphold the safety contract for `swap_nonoverlapping`.
-            unsafe { swap_nonoverlapping(x, y, 1) };
-            return;
-        }
+        attempt_swap_as_chunks!(usize);
+        attempt_swap_as_chunks!(u8);
     }
 
-    // Direct swapping, for the cases not going through the block optimization.
-    // SAFETY: the caller must guarantee that `x` and `y` are valid
-    // for writes, properly aligned, and non-overlapping.
-    unsafe {
-        let z = read(x);
-        copy_nonoverlapping(y, x, 1);
-        write(y, z);
-    }
+    // SAFETY: Same preconditions as this function
+    unsafe { swap_nonoverlapping_simple(x, y, count) }
 }
 
+/// Same behaviour and safety conditions as [`swap_nonoverlapping`]
+///
+/// LLVM can vectorize this (at least it can for the power-of-two-sized types
+/// `swap_nonoverlapping` tries to use) so no need to manually SIMD it.
 #[inline]
 #[rustc_const_unstable(feature = "const_swap", issue = "83163")]
-const unsafe fn swap_nonoverlapping_bytes(x: *mut u8, y: *mut u8, len: usize) {
-    // The approach here is to utilize simd to swap x & y efficiently. Testing reveals
-    // that swapping either 32 bytes or 64 bytes at a time is most efficient for Intel
-    // Haswell E processors. LLVM is more able to optimize if we give a struct a
-    // #[repr(simd)], even if we don't actually use this struct directly.
-    //
-    // FIXME repr(simd) broken on emscripten and redox
-    #[cfg_attr(not(any(target_os = "emscripten", target_os = "redox")), repr(simd))]
-    struct Block(u64, u64, u64, u64);
-    struct UnalignedBlock(u64, u64, u64, u64);
-
-    let block_size = mem::size_of::<Block>();
-
-    // Loop through x & y, copying them `Block` at a time
-    // The optimizer should unroll the loop fully for most types
-    // N.B. We can't use a for loop as the `range` impl calls `mem::swap` recursively
+const unsafe fn swap_nonoverlapping_simple<T>(x: *mut T, y: *mut T, count: usize) {
     let mut i = 0;
-    while i + block_size <= len {
-        // Create some uninitialized memory as scratch space
-        // Declaring `t` here avoids aligning the stack when this loop is unused
-        let mut t = mem::MaybeUninit::<Block>::uninit();
-        let t = t.as_mut_ptr() as *mut u8;
-
-        // SAFETY: As `i < len`, and as the caller must guarantee that `x` and `y` are valid
-        // for `len` bytes, `x + i` and `y + i` must be valid addresses, which fulfills the
-        // safety contract for `add`.
-        //
-        // Also, the caller must guarantee that `x` and `y` are valid for writes, properly aligned,
-        // and non-overlapping, which fulfills the safety contract for `copy_nonoverlapping`.
-        unsafe {
-            let x = x.add(i);
-            let y = y.add(i);
+    while i < count {
+        let x: &mut T =
+            // SAFETY: By precondition, `i` is in-bounds because it's below `n`
+            unsafe { &mut *x.add(i) };
+        let y: &mut T =
+            // SAFETY: By precondition, `i` is in-bounds because it's below `n`
+            // and it's distinct from `x` since the ranges are non-overlapping
+            unsafe { &mut *y.add(i) };
+        mem::swap_simple(x, y);
 
-            // Swap a block of bytes of x & y, using t as a temporary buffer
-            // This should be optimized into efficient SIMD operations where available
-            copy_nonoverlapping(x, t, block_size);
-            copy_nonoverlapping(y, x, block_size);
-            copy_nonoverlapping(t, y, block_size);
-        }
-        i += block_size;
-    }
-
-    if i < len {
-        // Swap any remaining bytes
-        let mut t = mem::MaybeUninit::<UnalignedBlock>::uninit();
-        let rem = len - i;
-
-        let t = t.as_mut_ptr() as *mut u8;
-
-        // SAFETY: see previous safety comment.
-        unsafe {
-            let x = x.add(i);
-            let y = y.add(i);
-
-            copy_nonoverlapping(x, t, rem);
-            copy_nonoverlapping(y, x, rem);
-            copy_nonoverlapping(t, y, rem);
-        }
+        i += 1;
     }
 }
 
diff --git a/src/doc/unstable-book/src/compiler-flags/check-cfg.md b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
new file mode 100644
index 00000000000..d7345ad0c33
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/check-cfg.md
@@ -0,0 +1,221 @@
+# `check-cfg`
+
+The tracking issue for this feature is: [#82450](https://github.com/rust-lang/rust/issues/82450).
+
+------------------------
+
+This feature allows you to enable complete or partial checking of configuration.
+
+`rustc` accepts the `--check-cfg` option, which specifies whether to check conditions and how to
+check them. The `--check-cfg` option takes a value, called the _check cfg specification_. The
+check cfg specification is parsed using the Rust metadata syntax, just as the `--cfg` option is.
+
+`--check-cfg` option can take one of two forms:
+
+1. `--check-cfg names(...)` enables checking condition names.
+2. `--check-cfg values(...)` enables checking the values within list-valued conditions.
+
+These two options are independent. `names` checks only the namespace of condition names
+while `values` checks only the namespace of the values of list-valued conditions.
+
+## The `names(...)` form
+
+The `names(...)` form enables checking the names. This form uses a named list:
+
+```bash
+rustc --check-cfg 'names(name1, name2, ... nameN)'
+```
+
+where each `name` is a bare identifier (has no quotes). The order of the names is not significant.
+
+If `--check-cfg names(...)` is specified at least once, then `rustc` will check all references to
+condition names. `rustc` will check every `#[cfg]` attribute, `#[cfg_attr]` attribute, `cfg` clause
+inside `#[link]` attribute and `cfg!(...)` call against the provided list of expected condition
+names. If a name is not present in this list, then `rustc` will report an `unexpected_cfgs` lint
+diagnostic. The default diagnostic level for this lint is `Warn`.
+
+If `--check-cfg names(...)` is not specified, then `rustc` will not check references to condition
+names.
+
+`--check-cfg names(...)` may be specified more than once. The result is that the list of valid
+condition names is merged across all options. It is legal for a condition name to be specified
+more than once; redundantly specifying a condition name has no effect.
+
+To enable checking condition names with an empty set of valid condition names, use the following
+form. The parentheses are required.
+
+```bash
+rustc --check-cfg 'names()'
+```
+
+Note that `--check-cfg 'names()'` is _not_ equivalent to omitting the option entirely.
+The first form enables checking condition names, while specifying that there are no valid
+condition names (outside of the set of well-known names defined by `rustc`). Omitting the
+`--check-cfg 'names(...)'` option does not enable checking condition names.
+
+Conditions that are enabled are implicitly valid; it is unnecessary (but legal) to specify a
+condition name as both enabled and valid. For example, the following invocations are equivalent:
+
+```bash
+# condition names will be checked, and 'has_time_travel' is valid
+rustc --cfg 'has_time_travel' --check-cfg 'names()'
+
+# condition names will be checked, and 'has_time_travel' is valid
+rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)'
+```
+
+In contrast, the following two invocations are _not_ equivalent:
+
+```bash
+# condition names will not be checked (because there is no --check-cfg names(...))
+rustc --cfg 'has_time_travel'
+
+# condition names will be checked, and 'has_time_travel' is both valid and enabled.
+rustc --cfg 'has_time_travel' --check-cfg 'names(has_time_travel)'
+```
+
+## The `values(...)` form
+
+The `values(...)` form enables checking the values within list-valued conditions. It has this
+form:
+
+```bash
+rustc --check-cfg `values(name, "value1", "value2", ... "valueN")'
+```
+
+where `name` is a bare identifier (has no quotes) and each `"value"` term is a quoted literal
+string. `name` specifies the name of the condition, such as `feature` or `target_os`.
+
+When the `values(...)` option is specified, `rustc` will check every `#[cfg(name = "value")]`
+attribute, `#[cfg_attr(name = "value")]` attribute, `#[link(name = "a", cfg(name = "value"))]`
+and `cfg!(name = "value")` call. It will check that the `"value"` specified is present in the
+list of expected values. If `"value"` is not in it, then `rustc` will report an `unexpected_cfgs`
+lint diagnostic. The default diagnostic level for this lint is `Warn`.
+
+The form `values()` is an error, because it does not specify a condition name.
+
+To enable checking of values, but to provide an empty set of valid values, use this form:
+
+```bash
+rustc --check-cfg `values(name)`
+```
+
+The `--check-cfg values(...)` option can be repeated, both for the same condition name and for
+different names. If it is repeated for the same condition name, then the sets of values for that
+condition are merged together.
+
+## Examples
+
+Consider this command line:
+
+```bash
+rustc --check-cfg 'names(feature)' \
+      --check-cfg 'values(feature,"lion","zebra")' \
+      --cfg 'feature="lion"' -Z unstable-options \
+      example.rs
+```
+
+This command line indicates that this crate has two features: `lion` and `zebra`. The `lion`
+feature is enabled, while the `zebra` feature is disabled. Consider compiling this code:
+
+```rust
+// This is expected, and tame_lion() will be compiled
+#[cfg(feature = "lion")]
+fn tame_lion(lion: Lion) {}
+
+// This is expected, and ride_zebra() will NOT be compiled.
+#[cfg(feature = "zebra")]
+fn ride_zebra(zebra: Zebra) {}
+
+// This is UNEXPECTED, and will cause a compiler warning (by default).
+#[cfg(feature = "platypus")]
+fn poke_platypus() {}
+
+// This is UNEXPECTED, because 'feechure' is not a known condition name,
+// and will cause a compiler warning (by default).
+#[cfg(feechure = "lion")]
+fn tame_lion() {}
+```
+
+> Note: The `--check-cfg names(feature)` option is necessary only to enable checking the condition
+> name, as in the last example. `feature` is a well-known (always-expected) condition name, and so
+> it is not necessary to specify it in a `--check-cfg 'names(...)'` option. That option can be
+> shortened to > `--check-cfg names()` in order to enable checking well-known condition names.
+
+### Example: Checking condition names, but not values
+
+```bash
+# This turns on checking for condition names, but not values, such as 'feature' values.
+rustc --check-cfg 'names(is_embedded, has_feathers)' \
+      --cfg has_feathers --cfg 'feature = "zapping"' -Z unstable-options
+```
+
+```rust
+#[cfg(is_embedded)]         // This is expected as "is_embedded" was provided in names()
+fn do_embedded() {}
+
+#[cfg(has_feathers)]        // This is expected as "has_feathers" was provided in names()
+fn do_features() {}
+
+#[cfg(has_mumble_frotz)]    // This is UNEXPECTED because names checking is enable and
+                            // "has_mumble_frotz" was not provided in names()
+fn do_mumble_frotz() {}
+
+#[cfg(feature = "lasers")]  // This doesn't raise a warning, because values checking for "feature"
+                            // was never used
+fn shoot_lasers() {}
+```
+
+### Example: Checking feature values, but not condition names
+
+```bash
+# This turns on checking for feature values, but not for condition names.
+rustc --check-cfg 'values(feature, "zapping", "lasers")' \
+      --cfg 'feature="zapping"' -Z unstable-options
+```
+
+```rust
+#[cfg(is_embedded)]         // This is doesn't raise a warning, because names checking was not
+                            // enable (ie not names())
+fn do_embedded() {}
+
+#[cfg(has_feathers)]        // Same as above, --check-cfg names(...) was never used so no name
+                            // checking is performed
+fn do_features() {}
+
+
+#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the values(feature) list
+fn shoot_lasers() {}
+
+#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in the
+                            // --check-cfg values(feature) list
+fn write_shakespeare() {}
+```
+
+### Example: Checking both condition names and feature values
+
+```bash
+# This turns on checking for feature values and for condition names.
+rustc --check-cfg 'names(is_embedded, has_feathers)' \
+      --check-cfg 'values(feature, "zapping", "lasers")' \
+      --cfg has_feathers --cfg 'feature="zapping"' -Z unstable-options
+```
+
+```rust
+#[cfg(is_embedded)]         // This is expected because "is_embedded" was provided in names()
+fn do_embedded() {}
+
+#[cfg(has_feathers)]        // This is expected because "has_feathers" was provided in names()
+fn do_features() {}
+
+#[cfg(has_mumble_frotz)]    // This is UNEXPECTED, because has_mumble_frotz is not in the
+                            // --check-cfg names(...) list
+fn do_mumble_frotz() {}
+
+#[cfg(feature = "lasers")]  // This is expected, "lasers" is in the values(feature) list
+fn shoot_lasers() {}
+
+#[cfg(feature = "monkeys")] // This is UNEXPECTED, because "monkeys" is not in
+                            // the values(feature) list
+fn write_shakespear() {}
+```
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 76994f2ee17..1e0c1e8f1f3 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1855,7 +1855,7 @@ fn clean_maybe_renamed_item(
             ItemKind::Fn(ref sig, ref generics, body_id) => {
                 clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
             }
-            ItemKind::Macro(ref macro_def) => {
+            ItemKind::Macro(ref macro_def, _) => {
                 let ty_vis = cx.tcx.visibility(def_id).clean(cx);
                 MacroItem(Macro {
                     source: display_macro_source(cx, name, macro_def, def_id, ty_vis),
diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs
index dff370ab750..58ca8869ea9 100644
--- a/src/librustdoc/clean/render_macro_matchers.rs
+++ b/src/librustdoc/clean/render_macro_matchers.rs
@@ -69,9 +69,7 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
         match rustc_parse::maybe_new_parser_from_source_str(&sess, file_name, snippet.clone()) {
             Ok(parser) => parser,
             Err(diagnostics) => {
-                for mut diagnostic in diagnostics {
-                    diagnostic.cancel();
-                }
+                drop(diagnostics);
                 return None;
             }
         };
@@ -79,7 +77,7 @@ fn snippet_equal_to_token(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Option<String
     // Reparse a single token tree.
     let mut reparsed_trees = match parser.parse_all_token_trees() {
         Ok(reparsed_trees) => reparsed_trees,
-        Err(mut diagnostic) => {
+        Err(diagnostic) => {
             diagnostic.cancel();
             return None;
         }
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 696397c5f67..a08732be1c5 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -551,10 +551,7 @@ crate fn make_test(
             let mut parser = match maybe_new_parser_from_source_str(&sess, filename, source) {
                 Ok(p) => p,
                 Err(errs) => {
-                    for mut err in errs {
-                        err.cancel();
-                    }
-
+                    drop(errs);
                     return (found_main, found_extern_crate, found_macro);
                 }
             };
@@ -594,7 +591,7 @@ crate fn make_test(
                         }
                     }
                     Ok(None) => break,
-                    Err(mut e) => {
+                    Err(e) => {
                         e.cancel();
                         break;
                     }
@@ -1164,7 +1161,7 @@ impl<'a, 'hir, 'tcx> intravisit::Visitor<'hir> for HirCollector<'a, 'hir, 'tcx>
 
     fn visit_item(&mut self, item: &'hir hir::Item<'_>) {
         let name = match &item.kind {
-            hir::ItemKind::Macro(ref macro_def) => {
+            hir::ItemKind::Macro(ref macro_def, _) => {
                 // FIXME(#88038): Non exported macros have historically not been tested,
                 // but we really ought to start testing them.
                 let def_id = item.def_id.to_def_id();
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 7eff725989c..9d3e58a3a66 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -720,12 +720,10 @@ fn run_renderer<'tcx, T: formats::FormatRenderer<'tcx>>(
             let mut msg =
                 tcx.sess.struct_err(&format!("couldn't generate documentation: {}", e.error));
             let file = e.file.display().to_string();
-            if file.is_empty() {
-                msg.emit()
-            } else {
-                msg.note(&format!("failed to create or modify \"{}\"", file)).emit()
+            if !file.is_empty() {
+                msg.note(&format!("failed to create or modify \"{}\"", file));
             }
-            Err(ErrorReported)
+            Err(msg.emit())
         }
     }
 }
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 33a1530d588..3ebd28e83b1 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -3,7 +3,7 @@
 //! [RFC 1946]: https://github.com/rust-lang/rfcs/blob/master/text/1946-intra-rustdoc-links.md
 
 use rustc_data_structures::{fx::FxHashMap, stable_set::FxHashSet};
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::def::{
     DefKind,
     Namespace::{self, *},
@@ -1434,7 +1434,7 @@ impl LinkCollector<'_, '_> {
     ) {
         // The resolved item did not match the disambiguator; give a better error than 'not found'
         let msg = format!("incompatible link kind for `{}`", path_str);
-        let callback = |diag: &mut DiagnosticBuilder<'_>, sp: Option<rustc_span::Span>| {
+        let callback = |diag: &mut Diagnostic, sp: Option<rustc_span::Span>| {
             let note = format!(
                 "this link resolved to {} {}, which is not {} {}",
                 resolved.article(),
@@ -1866,7 +1866,7 @@ fn report_diagnostic(
     lint: &'static Lint,
     msg: &str,
     DiagnosticInfo { item, ori_link: _, dox, link_range }: &DiagnosticInfo<'_>,
-    decorate: impl FnOnce(&mut DiagnosticBuilder<'_>, Option<rustc_span::Span>),
+    decorate: impl FnOnce(&mut Diagnostic, Option<rustc_span::Span>),
 ) {
     let hir_id = match DocContext::as_local_hir_id(tcx, item.def_id) {
         Some(hir_id) => hir_id,
@@ -2098,8 +2098,15 @@ fn resolution_failure(
                         )
                     }
                     ResolutionFailure::NoParentItem => {
-                        diag.level = rustc_errors::Level::Bug;
-                        "all intra-doc links should have a parent item".to_owned()
+                        // FIXME(eddyb) this doesn't belong here, whatever made
+                        // the `ResolutionFailure::NoParentItem` should emit an
+                        // immediate or delayed `span_bug` about the issue.
+                        tcx.sess.delay_span_bug(
+                            sp.unwrap_or(DUMMY_SP),
+                            "intra-doc link missing parent item",
+                        );
+
+                        "BUG: all intra-doc links should have a parent item".to_owned()
                     }
                     ResolutionFailure::MalformedGenerics(variant) => match variant {
                         MalformedGenerics::UnbalancedAngleBrackets => {
@@ -2233,7 +2240,7 @@ fn ambiguity_error(
 /// disambiguator.
 fn suggest_disambiguator(
     res: Res,
-    diag: &mut DiagnosticBuilder<'_>,
+    diag: &mut Diagnostic,
     path_str: &str,
     ori_link: &str,
     sp: Option<rustc_span::Span>,
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index e8b3a0929db..1693034db0e 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -325,7 +325,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
                 om.items.push((item, renamed))
             }
-            hir::ItemKind::Macro(ref macro_def) => {
+            hir::ItemKind::Macro(ref macro_def, _) => {
                 // `#[macro_export] macro_rules!` items are handled seperately in `visit()`,
                 // above, since they need to be documented at the module top level. Accordingly,
                 // we only want to handle macros if one of three conditions holds:
diff --git a/src/test/codegen/swap-large-types.rs b/src/test/codegen/swap-large-types.rs
new file mode 100644
index 00000000000..535d301a3d2
--- /dev/null
+++ b/src/test/codegen/swap-large-types.rs
@@ -0,0 +1,64 @@
+// compile-flags: -O
+// only-x86_64
+// ignore-debug: the debug assertions get in the way
+
+#![crate_type = "lib"]
+
+use std::mem::swap;
+use std::ptr::{read, copy_nonoverlapping, write};
+
+type KeccakBuffer = [[u64; 5]; 5];
+
+// A basic read+copy+write swap implementation ends up copying one of the values
+// to stack for large types, which is completely unnecessary as the lack of
+// overlap means we can just do whatever fits in registers at a time.
+
+// CHECK-LABEL: @swap_basic
+#[no_mangle]
+pub fn swap_basic(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
+// CHECK: alloca [5 x [5 x i64]]
+
+    // SAFETY: exclusive references are always valid to read/write,
+    // are non-overlapping, and nothing here panics so it's drop-safe.
+    unsafe {
+        let z = read(x);
+        copy_nonoverlapping(y, x, 1);
+        write(y, z);
+    }
+}
+
+// This test verifies that the library does something smarter, and thus
+// doesn't need any scratch space on the stack.
+
+// CHECK-LABEL: @swap_std
+#[no_mangle]
+pub fn swap_std(x: &mut KeccakBuffer, y: &mut KeccakBuffer) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i64>
+// CHECK: store <{{[0-9]+}} x i64>
+    swap(x, y)
+}
+
+// CHECK-LABEL: @swap_slice
+#[no_mangle]
+pub fn swap_slice(x: &mut [KeccakBuffer], y: &mut [KeccakBuffer]) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i64>
+// CHECK: store <{{[0-9]+}} x i64>
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
+
+type OneKilobyteBuffer = [u8; 1024];
+
+// CHECK-LABEL: @swap_1kb_slices
+#[no_mangle]
+pub fn swap_1kb_slices(x: &mut [OneKilobyteBuffer], y: &mut [OneKilobyteBuffer]) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i8>
+// CHECK: store <{{[0-9]+}} x i8>
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
diff --git a/src/test/codegen/swap-simd-types.rs b/src/test/codegen/swap-simd-types.rs
new file mode 100644
index 00000000000..c90b277eb44
--- /dev/null
+++ b/src/test/codegen/swap-simd-types.rs
@@ -0,0 +1,32 @@
+// compile-flags: -O -C target-feature=+avx
+// only-x86_64
+// ignore-debug: the debug assertions get in the way
+
+#![crate_type = "lib"]
+
+use std::mem::swap;
+
+// SIMD types are highly-aligned already, so make sure the swap code leaves their
+// types alone and doesn't pessimize them (such as by swapping them as `usize`s).
+extern crate core;
+use core::arch::x86_64::__m256;
+
+// CHECK-LABEL: @swap_single_m256
+#[no_mangle]
+pub fn swap_single_m256(x: &mut __m256, y: &mut __m256) {
+// CHECK-NOT: alloca
+// CHECK: load <8 x float>{{.+}}align 32
+// CHECK: store <8 x float>{{.+}}align 32
+    swap(x, y)
+}
+
+// CHECK-LABEL: @swap_m256_slice
+#[no_mangle]
+pub fn swap_m256_slice(x: &mut [__m256], y: &mut [__m256]) {
+// CHECK-NOT: alloca
+// CHECK: load <8 x float>{{.+}}align 32
+// CHECK: store <8 x float>{{.+}}align 32
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
diff --git a/src/test/codegen/swap-small-types.rs b/src/test/codegen/swap-small-types.rs
index 6205e6a6559..2f375844cc7 100644
--- a/src/test/codegen/swap-small-types.rs
+++ b/src/test/codegen/swap-small-types.rs
@@ -16,3 +16,47 @@ pub fn swap_rgb48(x: &mut RGB48, y: &mut RGB48) {
 // CHECK: store i48
     swap(x, y)
 }
+
+// LLVM doesn't vectorize a loop over 3-byte elements,
+// so we chunk it down to bytes and loop over those instead.
+type RGB24 = [u8; 3];
+
+// CHECK-LABEL: @swap_rgb24_slices
+#[no_mangle]
+pub fn swap_rgb24_slices(x: &mut [RGB24], y: &mut [RGB24]) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i8>
+// CHECK: store <{{[0-9]+}} x i8>
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
+
+// This one has a power-of-two size, so we iterate over it directly
+type RGBA32 = [u8; 4];
+
+// CHECK-LABEL: @swap_rgba32_slices
+#[no_mangle]
+pub fn swap_rgba32_slices(x: &mut [RGBA32], y: &mut [RGBA32]) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i32>
+// CHECK: store <{{[0-9]+}} x i32>
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
+
+// Strings have a non-power-of-two size, but have pointer alignment,
+// so we swap usizes instead of dropping all the way down to bytes.
+const _: () = assert!(!std::mem::size_of::<String>().is_power_of_two());
+
+// CHECK-LABEL: @swap_string_slices
+#[no_mangle]
+pub fn swap_string_slices(x: &mut [String], y: &mut [String]) {
+// CHECK-NOT: alloca
+// CHECK: load <{{[0-9]+}} x i64>
+// CHECK: store <{{[0-9]+}} x i64>
+    if x.len() == y.len() {
+        x.swap_with_slice(y);
+    }
+}
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index 8b0cebfa60e..a37d3a32571 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -42,7 +42,7 @@ fn parse_expr(ps: &ParseSess, src: &str) -> Option<P<Expr>> {
 
     let mut p =
         new_parser_from_source_str(ps, FileName::Custom(src_as_string.clone()), src_as_string);
-    p.parse_expr().map_err(|mut e| e.cancel()).ok()
+    p.parse_expr().map_err(|e| e.cancel()).ok()
 }
 
 // Helper functions for building exprs
diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.rs b/src/test/ui/auto-traits/suspicious-impls-lint.rs
index 1026a35a455..1574a7e02e9 100644
--- a/src/test/ui/auto-traits/suspicious-impls-lint.rs
+++ b/src/test/ui/auto-traits/suspicious-impls-lint.rs
@@ -1,5 +1,7 @@
 #![deny(suspicious_auto_trait_impls)]
 
+use std::marker::PhantomData;
+
 struct MayImplementSendOk<T>(T);
 unsafe impl<T: Send> Send for MayImplementSendOk<T> {} // ok
 
@@ -31,4 +33,12 @@ unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
 //~^ ERROR
 //~| WARNING this will change its meaning
 
+pub struct WithPhantomDataNonSend<T, U>(PhantomData<*const T>, U);
+unsafe impl<T> Send for WithPhantomDataNonSend<T, i8> {} // ok
+
+pub struct WithPhantomDataSend<T, U>(PhantomData<T>, U);
+unsafe impl<T> Send for WithPhantomDataSend<*const T, i8> {}
+//~^ ERROR
+//~| WARNING this will change its meaning
+
 fn main() {}
diff --git a/src/test/ui/auto-traits/suspicious-impls-lint.stderr b/src/test/ui/auto-traits/suspicious-impls-lint.stderr
index f91aa862271..084bfef49c0 100644
--- a/src/test/ui/auto-traits/suspicious-impls-lint.stderr
+++ b/src/test/ui/auto-traits/suspicious-impls-lint.stderr
@@ -1,5 +1,5 @@
 error: cross-crate traits with a default impl, like `Send`, should not be specialized
-  --> $DIR/suspicious-impls-lint.rs:7:1
+  --> $DIR/suspicious-impls-lint.rs:9:1
    |
 LL | unsafe impl<T: Send> Send for MayImplementSendErr<&T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -12,14 +12,14 @@ LL | #![deny(suspicious_auto_trait_impls)]
    = warning: this will change its meaning in a future release!
    = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
 note: try using the same sequence of generic parameters as the struct definition
-  --> $DIR/suspicious-impls-lint.rs:6:1
+  --> $DIR/suspicious-impls-lint.rs:8:1
    |
 LL | struct MayImplementSendErr<T>(T);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: `&T` is not a generic parameter
 
 error: cross-crate traits with a default impl, like `Send`, should not be specialized
-  --> $DIR/suspicious-impls-lint.rs:19:1
+  --> $DIR/suspicious-impls-lint.rs:21:1
    |
 LL | unsafe impl Send for ContainsVec<i32> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -27,14 +27,14 @@ LL | unsafe impl Send for ContainsVec<i32> {}
    = warning: this will change its meaning in a future release!
    = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
 note: try using the same sequence of generic parameters as the struct definition
-  --> $DIR/suspicious-impls-lint.rs:18:1
+  --> $DIR/suspicious-impls-lint.rs:20:1
    |
 LL | struct ContainsVec<T>(Vec<T>);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: `i32` is not a generic parameter
 
 error: cross-crate traits with a default impl, like `Send`, should not be specialized
-  --> $DIR/suspicious-impls-lint.rs:30:1
+  --> $DIR/suspicious-impls-lint.rs:32:1
    |
 LL | unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -42,11 +42,26 @@ LL | unsafe impl<T: Send> Send for TwoParamsSame<T, T> {}
    = warning: this will change its meaning in a future release!
    = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
 note: try using the same sequence of generic parameters as the struct definition
-  --> $DIR/suspicious-impls-lint.rs:29:1
+  --> $DIR/suspicious-impls-lint.rs:31:1
    |
 LL | struct TwoParamsSame<T, U>(T, U);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: `T` is mentioned multiple times
 
-error: aborting due to 3 previous errors
+error: cross-crate traits with a default impl, like `Send`, should not be specialized
+  --> $DIR/suspicious-impls-lint.rs:40:1
+   |
+LL | unsafe impl<T> Send for WithPhantomDataSend<*const T, i8> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = warning: this will change its meaning in a future release!
+   = note: for more information, see issue #93367 <https://github.com/rust-lang/rust/issues/93367>
+note: try using the same sequence of generic parameters as the struct definition
+  --> $DIR/suspicious-impls-lint.rs:39:1
+   |
+LL | pub struct WithPhantomDataSend<T, U>(PhantomData<T>, U);
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `*const T` is not a generic parameter
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/check-cfg/invalid-cfg-name.stderr b/src/test/ui/check-cfg/invalid-cfg-name.stderr
index 2587685afa0..2bd1821c942 100644
--- a/src/test/ui/check-cfg/invalid-cfg-name.stderr
+++ b/src/test/ui/check-cfg/invalid-cfg-name.stderr
@@ -2,7 +2,7 @@ warning: unexpected `cfg` condition name
   --> $DIR/invalid-cfg-name.rs:7:7
    |
 LL | #[cfg(widnows)]
-   |       ^^^^^^^
+   |       ^^^^^^^ help: did you mean: `windows`
    |
    = note: `#[warn(unexpected_cfgs)]` on by default
 
diff --git a/src/test/ui/check-cfg/invalid-cfg-value.stderr b/src/test/ui/check-cfg/invalid-cfg-value.stderr
index c591d8474a2..bc2c053fed6 100644
--- a/src/test/ui/check-cfg/invalid-cfg-value.stderr
+++ b/src/test/ui/check-cfg/invalid-cfg-value.stderr
@@ -5,6 +5,7 @@ LL | #[cfg(feature = "sedre")]
    |       ^^^^^^^^^^^^^^^^^
    |
    = note: `#[warn(unexpected_cfgs)]` on by default
+   = note: expected values for `feature` are: full, rand, serde
 
 warning: 1 warning emitted
 
diff --git a/src/test/ui/check-cfg/mix.rs b/src/test/ui/check-cfg/mix.rs
new file mode 100644
index 00000000000..26c735c4a10
--- /dev/null
+++ b/src/test/ui/check-cfg/mix.rs
@@ -0,0 +1,50 @@
+// This test checks the combination of well known names, their activation via names(), the usage of
+// partial values() with a --cfg and test that we also correctly lint on the `cfg!` macro and
+// `cfg_attr` attribute.
+//
+// check-pass
+// compile-flags: --check-cfg=names() --check-cfg=values(feature,"foo") --cfg feature="bar" -Z unstable-options
+
+#[cfg(windows)]
+fn do_windows_stuff() {}
+
+#[cfg(widnows)]
+//~^ WARNING unexpected `cfg` condition name
+fn do_windows_stuff() {}
+
+#[cfg(feature = "foo")]
+fn use_foo() {}
+
+#[cfg(feature = "bar")]
+fn use_bar() {}
+
+#[cfg(feature = "zebra")]
+//~^ WARNING unexpected `cfg` condition value
+fn use_zebra() {}
+
+#[cfg_attr(uu, test)]
+//~^ WARNING unexpected `cfg` condition name
+fn do_test() {}
+
+#[cfg_attr(feature = "foo", no_mangle)]
+fn do_test_foo() {}
+
+fn test_cfg_macro() {
+    cfg!(windows);
+    cfg!(widnows);
+    //~^ WARNING unexpected `cfg` condition name
+    cfg!(feature = "foo");
+    cfg!(feature = "bar");
+    cfg!(feature = "zebra");
+    //~^ WARNING unexpected `cfg` condition value
+    cfg!(xxx = "foo");
+    //~^ WARNING unexpected `cfg` condition name
+    cfg!(xxx);
+    //~^ WARNING unexpected `cfg` condition name
+    cfg!(any(xxx, windows));
+    //~^ WARNING unexpected `cfg` condition name
+    cfg!(any(feature = "bad", windows));
+    //~^ WARNING unexpected `cfg` condition value
+}
+
+fn main() {}
diff --git a/src/test/ui/check-cfg/mix.stderr b/src/test/ui/check-cfg/mix.stderr
new file mode 100644
index 00000000000..b273be77422
--- /dev/null
+++ b/src/test/ui/check-cfg/mix.stderr
@@ -0,0 +1,66 @@
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:11:7
+   |
+LL | #[cfg(widnows)]
+   |       ^^^^^^^ help: did you mean: `windows`
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition value
+  --> $DIR/mix.rs:21:7
+   |
+LL | #[cfg(feature = "zebra")]
+   |       ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: bar, foo
+
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:25:12
+   |
+LL | #[cfg_attr(uu, test)]
+   |            ^^
+
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:34:10
+   |
+LL |     cfg!(widnows);
+   |          ^^^^^^^ help: did you mean: `windows`
+
+warning: unexpected `cfg` condition value
+  --> $DIR/mix.rs:38:10
+   |
+LL |     cfg!(feature = "zebra");
+   |          ^^^^^^^^^^^^^^^^^
+   |
+   = note: expected values for `feature` are: bar, foo
+
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:40:10
+   |
+LL |     cfg!(xxx = "foo");
+   |          ^^^^^^^^^^^
+
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:42:10
+   |
+LL |     cfg!(xxx);
+   |          ^^^
+
+warning: unexpected `cfg` condition name
+  --> $DIR/mix.rs:44:14
+   |
+LL |     cfg!(any(xxx, windows));
+   |              ^^^
+
+warning: unexpected `cfg` condition value
+  --> $DIR/mix.rs:46:14
+   |
+LL |     cfg!(any(feature = "bad", windows));
+   |              ^^^^^^^^^^-----
+   |                        |
+   |                        help: did you mean: `"bar"`
+   |
+   = note: expected values for `feature` are: bar, foo
+
+warning: 9 warnings emitted
+
diff --git a/src/test/ui/check-cfg/no-values.rs b/src/test/ui/check-cfg/no-values.rs
new file mode 100644
index 00000000000..2440757e52d
--- /dev/null
+++ b/src/test/ui/check-cfg/no-values.rs
@@ -0,0 +1,10 @@
+// Check that we detect unexpected value when none are allowed
+//
+// check-pass
+// compile-flags: --check-cfg=values(feature) -Z unstable-options
+
+#[cfg(feature = "foo")]
+//~^ WARNING unexpected `cfg` condition value
+fn do_foo() {}
+
+fn main() {}
diff --git a/src/test/ui/check-cfg/no-values.stderr b/src/test/ui/check-cfg/no-values.stderr
new file mode 100644
index 00000000000..ea1c9107d4c
--- /dev/null
+++ b/src/test/ui/check-cfg/no-values.stderr
@@ -0,0 +1,11 @@
+warning: unexpected `cfg` condition value
+  --> $DIR/no-values.rs:6:7
+   |
+LL | #[cfg(feature = "foo")]
+   |       ^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+   = note: no expected value for `feature`
+
+warning: 1 warning emitted
+
diff --git a/src/test/ui/check-cfg/well-known-names.rs b/src/test/ui/check-cfg/well-known-names.rs
new file mode 100644
index 00000000000..a66568a2ffd
--- /dev/null
+++ b/src/test/ui/check-cfg/well-known-names.rs
@@ -0,0 +1,27 @@
+// This test checks that we lint on non well known names and that we don't lint on well known names
+//
+// check-pass
+// compile-flags: --check-cfg=names() -Z unstable-options
+
+#[cfg(target_oz = "linux")]
+//~^ WARNING unexpected `cfg` condition name
+fn target_os_misspell() {}
+
+#[cfg(target_os = "linux")]
+fn target_os() {}
+
+#[cfg(features = "foo")]
+//~^ WARNING unexpected `cfg` condition name
+fn feature_misspell() {}
+
+#[cfg(feature = "foo")]
+fn feature() {}
+
+#[cfg(uniw)]
+//~^ WARNING unexpected `cfg` condition name
+fn unix_misspell() {}
+
+#[cfg(unix)]
+fn unix() {}
+
+fn main() {}
diff --git a/src/test/ui/check-cfg/well-known-names.stderr b/src/test/ui/check-cfg/well-known-names.stderr
new file mode 100644
index 00000000000..bdbe4d29d30
--- /dev/null
+++ b/src/test/ui/check-cfg/well-known-names.stderr
@@ -0,0 +1,26 @@
+warning: unexpected `cfg` condition name
+  --> $DIR/well-known-names.rs:6:7
+   |
+LL | #[cfg(target_oz = "linux")]
+   |       ---------^^^^^^^^^^
+   |       |
+   |       help: did you mean: `target_os`
+   |
+   = note: `#[warn(unexpected_cfgs)]` on by default
+
+warning: unexpected `cfg` condition name
+  --> $DIR/well-known-names.rs:13:7
+   |
+LL | #[cfg(features = "foo")]
+   |       --------^^^^^^^^
+   |       |
+   |       help: did you mean: `feature`
+
+warning: unexpected `cfg` condition name
+  --> $DIR/well-known-names.rs:20:7
+   |
+LL | #[cfg(uniw)]
+   |       ^^^^ help: did you mean: `unix`
+
+warning: 3 warnings emitted
+
diff --git a/src/test/ui/debuginfo-emit-llvm-ir-and-split-debuginfo.rs b/src/test/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs
index 043011b3316..043011b3316 100644
--- a/src/test/ui/debuginfo-emit-llvm-ir-and-split-debuginfo.rs
+++ b/src/test/ui/debuginfo/debuginfo-emit-llvm-ir-and-split-debuginfo.rs
diff --git a/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs b/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs
new file mode 100644
index 00000000000..833a4726acb
--- /dev/null
+++ b/src/test/ui/debuginfo/debuginfo_with_uninhabitable_field_and_unsized.rs
@@ -0,0 +1,29 @@
+// check-pass
+// compile-flags: -Cdebuginfo=2
+// fixes issue #94149
+
+#![allow(dead_code)]
+
+pub fn main() {
+    let _ = Foo::<dyn FooTrait>::new();
+}
+
+pub struct Foo<T: FooTrait + ?Sized> {
+    base: FooBase,
+    value: T,
+}
+
+impl<T: FooTrait + ?Sized> Foo<T> {
+    pub fn new() -> Box<Foo<T>> {
+        todo!()
+    }
+}
+
+pub trait FooTrait {}
+
+pub struct FooBase {
+    cls: Bar,
+}
+
+// Bar *must* be a fieldless enum
+pub enum Bar {}
diff --git a/src/test/ui/traits/copy-impl-cannot-normalize.rs b/src/test/ui/traits/copy-impl-cannot-normalize.rs
new file mode 100644
index 00000000000..a78ff046e97
--- /dev/null
+++ b/src/test/ui/traits/copy-impl-cannot-normalize.rs
@@ -0,0 +1,25 @@
+trait TraitFoo {
+    type Bar;
+}
+
+struct Foo<T>
+where
+    T: TraitFoo,
+{
+    inner: T::Bar,
+}
+
+impl<T> Clone for Foo<T>
+where
+    T: TraitFoo,
+    T::Bar: Clone,
+{
+    fn clone(&self) -> Self {
+        Self { inner: self.inner.clone() }
+    }
+}
+
+impl<T> Copy for Foo<T> {}
+//~^ ERROR the trait bound `T: TraitFoo` is not satisfied
+
+fn main() {}
diff --git a/src/test/ui/traits/copy-impl-cannot-normalize.stderr b/src/test/ui/traits/copy-impl-cannot-normalize.stderr
new file mode 100644
index 00000000000..cc540ea905a
--- /dev/null
+++ b/src/test/ui/traits/copy-impl-cannot-normalize.stderr
@@ -0,0 +1,14 @@
+error[E0277]: the trait bound `T: TraitFoo` is not satisfied
+  --> $DIR/copy-impl-cannot-normalize.rs:22:1
+   |
+LL | impl<T> Copy for Foo<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `TraitFoo` is not implemented for `T`
+   |
+help: consider restricting type parameter `T`
+   |
+LL | impl<T: TraitFoo> Copy for Foo<T> {}
+   |       ++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 8b79f1600ae..a20aa12c9ff 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -6,7 +6,7 @@ use clippy_utils::{
 };
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Block, Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -489,7 +489,7 @@ fn emit_branches_sharing_code_lint(
         add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit();
     }
 
-    let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| {
+    let add_optional_msgs = |diag: &mut Diagnostic| {
         if add_expr_note {
             diag.note("The end suggestion probably needs some adjustments to use the expression result correctly");
         }
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index a00361e6062..16173580fd4 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -628,9 +628,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                 let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
                     Ok(p) => p,
                     Err(errs) => {
-                        for mut err in errs {
-                            err.cancel();
-                        }
+                        drop(errs);
                         return false;
                     },
                 };
@@ -668,7 +666,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
                             _ => {},
                         },
                         Ok(None) => break,
-                        Err(mut e) => {
+                        Err(e) => {
                             e.cancel();
                             return false;
                         },
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 5e4cde553b5..d5430a8c917 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -1,7 +1,7 @@
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 
-use rustc_errors::DiagnosticBuilder;
+use rustc_errors::Diagnostic;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
@@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
 
         fn suggestion<'tcx>(
             cx: &LateContext<'tcx>,
-            diag: &mut DiagnosticBuilder<'_>,
+            diag: &mut Diagnostic,
             generics_span: Span,
             generics_suggestion_span: Span,
             target: &ImplicitHasherType<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index df69d3dcc51..dd7177e0131 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -1,7 +1,7 @@
 //! checks for `#[inline]` on trait methods without bodies
 
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::sugg::DiagnosticExt;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index ebd4fb0bf51..ebfd908a6fb 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -6,7 +6,7 @@ use clippy_utils::{get_trait_def_id, is_self, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Node, PatKind, QPath, TyKind};
 use rustc_hir::{HirIdMap, HirIdSet};
@@ -196,10 +196,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                     }
 
                     // Dereference suggestion
-                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
+                    let sugg = |diag: &mut Diagnostic| {
                         if let ty::Adt(def, ..) = ty.kind() {
                             if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
-                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
+                                if can_type_implement_copy(cx.tcx, cx.param_env, ty, traits::ObligationCause::dummy_with_span(span)).is_ok() {
                                     diag.span_help(span, "consider marking this type as `Copy`");
                                 }
                             }
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index f86af7a7bb6..4cb79648ae3 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::return_ty;
 use clippy_utils::source::snippet;
-use clippy_utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::sugg::DiagnosticExt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
index 86911483137..dc48ea3f4f9 100644
--- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
@@ -373,7 +373,7 @@ fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
             let item_ty = cx.tcx.type_of(did);
             println!("function of type {:#?}", item_ty);
         },
-        hir::ItemKind::Macro(ref macro_def) => {
+        hir::ItemKind::Macro(ref macro_def, _) => {
             if macro_def.macro_rules {
                 println!("macro introduced by `macro_rules!`");
             } else {
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 1fa6301ebd7..a328ddda5ae 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -534,7 +534,7 @@ impl Write {
             match parser
                 .parse_expr()
                 .map(rustc_ast::ptr::P::into_inner)
-                .map_err(|mut e| e.cancel())
+                .map_err(|e| e.cancel())
             {
                 // write!(e, ...)
                 Ok(p) if parser.eat(&token::Comma) => Some(p),
@@ -563,7 +563,7 @@ impl Write {
             }
 
             let comma_span = parser.prev_token.span;
-            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
+            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|err| err.cancel()) {
                 expr
             } else {
                 return (Some(fmtstr), None);
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index ca222c3d669..a927788e6a4 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -8,13 +8,13 @@
 //! Thank you!
 //! ~The `INTERNAL_METADATA_COLLECTOR` lint
 
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::HirId;
 use rustc_lint::{LateContext, Lint, LintContext};
 use rustc_span::source_map::{MultiSpan, Span};
 use std::env;
 
-fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
+fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
     if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
         if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
             diag.help(&format!(
@@ -145,7 +145,7 @@ pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str
 where
     C: LintContext,
     S: Into<MultiSpan>,
-    F: FnOnce(&mut DiagnosticBuilder<'_>),
+    F: FnOnce(&mut Diagnostic),
 {
     cx.struct_span_lint(lint, sp, |diag| {
         let mut diag = diag.build(msg);
@@ -169,7 +169,7 @@ pub fn span_lint_hir_and_then(
     hir_id: HirId,
     sp: impl Into<MultiSpan>,
     msg: &str,
-    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
+    f: impl FnOnce(&mut Diagnostic),
 ) {
     cx.tcx.struct_span_lint_hir(lint, hir_id, sp, |diag| {
         let mut diag = diag.build(msg);
@@ -219,7 +219,7 @@ pub fn span_lint_and_sugg<'a, T: LintContext>(
 /// appear once per
 /// replacement. In human-readable format though, it only appears once before
 /// the whole suggestion.
-pub fn multispan_sugg<I>(diag: &mut DiagnosticBuilder<'_>, help_msg: &str, sugg: I)
+pub fn multispan_sugg<I>(diag: &mut Diagnostic, help_msg: &str, sugg: I)
 where
     I: IntoIterator<Item = (Span, String)>,
 {
@@ -232,7 +232,7 @@ where
 /// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141).
 /// Suggestions with multiple spans will be silently ignored.
 pub fn multispan_sugg_with_applicability<I>(
-    diag: &mut DiagnosticBuilder<'_>,
+    diag: &mut Diagnostic,
     help_msg: &str,
     applicability: Applicability,
     sugg: I,
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index fa63ddff253..63c442e7008 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -673,8 +673,8 @@ fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
         })
 }
 
-/// Convenience extension trait for `DiagnosticBuilder`.
-pub trait DiagnosticBuilderExt<T: LintContext> {
+/// Convenience extension trait for `Diagnostic`.
+pub trait DiagnosticExt<T: LintContext> {
     /// Suggests to add an attribute to an item.
     ///
     /// Correctly handles indentation of the attribute and item.
@@ -721,7 +721,7 @@ pub trait DiagnosticBuilderExt<T: LintContext> {
     fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
 }
 
-impl<T: LintContext> DiagnosticBuilderExt<T> for rustc_errors::DiagnosticBuilder<'_> {
+impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
     fn suggest_item_with_attr<D: Display + ?Sized>(
         &mut self,
         cx: &T,
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index 306ea50258d..a83c8ba0b64 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -15,7 +15,7 @@ extern crate macro_use_helper as mac;
 extern crate proc_macro_derive as mini_mac;
 
 mod a {
-    use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};
+    use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};
     use mac;
     use mini_mac::ClippyMiniMacroTest;
     use mini_mac;
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
index f8c86c8d917..9028a636e7f 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.stderr
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -2,7 +2,7 @@ error: `macro_use` attributes are no longer needed in the Rust 2018 edition
   --> $DIR/macro_use_imports.rs:18:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, inner_mod_macro, function_macro, ty_macro, pub_in_private_macro};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
    |
    = note: `-D clippy::macro-use-imports` implied by `-D warnings`
 
diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs
index 9c964b274e0..d4bddd95785 100644
--- a/src/tools/rustfmt/src/modules.rs
+++ b/src/tools/rustfmt/src/modules.rs
@@ -439,7 +439,7 @@ impl<'ast, 'sess, 'c> ModResolver<'ast, 'sess> {
                 }
             }
             Err(mod_err) if !mods_outside_ast.is_empty() => {
-                if let ModError::ParserError(mut e) = mod_err {
+                if let ModError::ParserError(e) = mod_err {
                     e.cancel();
                 }
                 Ok(Some(SubModKind::MultiExternal(mods_outside_ast)))
diff --git a/src/tools/rustfmt/src/parse/macros/cfg_if.rs b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
index e10fbe64bcd..306b6bb745e 100644
--- a/src/tools/rustfmt/src/parse/macros/cfg_if.rs
+++ b/src/tools/rustfmt/src/parse/macros/cfg_if.rs
@@ -57,7 +57,7 @@ fn parse_cfg_if_inner<'a>(
             let item = match parser.parse_item(ForceCollect::No) {
                 Ok(Some(item_ptr)) => item_ptr.into_inner(),
                 Ok(None) => continue,
-                Err(mut err) => {
+                Err(err) => {
                     err.cancel();
                     parser.sess.span_diagnostic.reset_err_count();
                     return Err(
diff --git a/src/tools/rustfmt/src/parse/macros/lazy_static.rs b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
index 9c8651aa3fa..4c541de04be 100644
--- a/src/tools/rustfmt/src/parse/macros/lazy_static.rs
+++ b/src/tools/rustfmt/src/parse/macros/lazy_static.rs
@@ -23,7 +23,7 @@ pub(crate) fn parse_lazy_static(
                         val
                     }
                 }
-                Err(mut err) => {
+                Err(err) => {
                     err.cancel();
                     parser.sess.span_diagnostic.reset_err_count();
                     return None;
diff --git a/src/tools/rustfmt/src/parse/macros/mod.rs b/src/tools/rustfmt/src/parse/macros/mod.rs
index 2e9ce1d35f4..fd738908170 100644
--- a/src/tools/rustfmt/src/parse/macros/mod.rs
+++ b/src/tools/rustfmt/src/parse/macros/mod.rs
@@ -36,7 +36,7 @@ fn parse_macro_arg<'a, 'b: 'a>(parser: &'a mut Parser<'b>) -> Option<MacroArg> {
                         return Some(MacroArg::$macro_arg($f(x)?));
                     }
                 }
-                Err(mut e) => {
+                Err(e) => {
                     e.cancel();
                     parser.sess.span_diagnostic.reset_err_count();
                 }
diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs
index 657217633f4..f0944a88d2f 100644
--- a/src/tools/rustfmt/src/parse/parser.rs
+++ b/src/tools/rustfmt/src/parse/parser.rs
@@ -115,7 +115,7 @@ impl<'a> Parser<'a> {
             match parser.parse_mod(&TokenKind::Eof) {
                 Ok(result) => Some(result),
                 Err(mut e) => {
-                    sess.emit_or_cancel_diagnostic(&mut e);
+                    e.emit();
                     if sess.can_reset_errors() {
                         sess.reset_errors();
                     }
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 7fc3778376c..40a6d708d8c 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -60,7 +60,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
         None
     }
     fn emit_diagnostic(&mut self, db: &Diagnostic) {
-        if db.level == DiagnosticLevel::Fatal {
+        if db.level() == DiagnosticLevel::Fatal {
             return self.handle_non_ignoreable_error(db);
         }
         if let Some(primary_span) = &db.span.primary_span() {
@@ -230,17 +230,6 @@ impl ParseSess {
         }
     }
 
-    pub(crate) fn emit_or_cancel_diagnostic(&self, diagnostic: &mut Diagnostic) {
-        self.parse_sess.span_diagnostic.emit_diagnostic(diagnostic);
-        // The Handler will check whether the diagnostic should be emitted
-        // based on the user's rustfmt configuration and the originating file
-        // that caused the parser error. If the Handler determined it should skip
-        // emission then we need to ensure the diagnostic is cancelled.
-        if !diagnostic.cancelled() {
-            diagnostic.cancel();
-        }
-    }
-
     pub(super) fn can_reset_errors(&self) -> bool {
         self.can_reset_errors.load(Ordering::Acquire)
     }
@@ -292,7 +281,7 @@ mod tests {
         use super::*;
         use crate::config::IgnoreList;
         use crate::utils::mk_sp;
-        use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName, DUMMY_SP};
+        use rustc_span::{FileName as SourceMapFileName, MultiSpan, RealFileName};
         use std::path::PathBuf;
         use std::sync::atomic::AtomicU32;
 
@@ -310,16 +299,12 @@ mod tests {
         }
 
         fn build_diagnostic(level: DiagnosticLevel, span: Option<MultiSpan>) -> Diagnostic {
-            Diagnostic {
-                level,
-                code: None,
-                message: vec![],
-                children: vec![],
-                suggestions: Ok(vec![]),
-                span: span.unwrap_or_else(MultiSpan::new),
-                sort_span: DUMMY_SP,
-                is_lint: false,
+            let mut diag = Diagnostic::new(level, "");
+            diag.message.clear();
+            if let Some(span) = span {
+                diag.span = span;
             }
+            diag
         }
 
         fn build_emitter(