about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_lint/src/types.rs148
-rw-r--r--compiler/rustc_middle/src/query/mod.rs7
-rw-r--r--compiler/rustc_middle/src/traits/mod.rs2
-rw-r--r--compiler/rustc_target/src/lib.rs1
-rw-r--r--compiler/rustc_target/src/spec/aarch64_apple_darwin.rs2
-rw-r--r--compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs11
-rw-r--r--compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs12
-rw-r--r--compiler/rustc_target/src/spec/armv7_linux_androideabi.rs2
-rw-r--r--compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs6
-rw-r--r--compiler/rustc_target/src/spec/avr_gnu_base.rs9
-rw-r--r--compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs2
-rw-r--r--compiler/rustc_target/src/spec/fuchsia_base.rs33
-rw-r--r--compiler/rustc_target/src/spec/hermit_base.rs9
-rw-r--r--compiler/rustc_target/src/spec/i686_apple_darwin.rs2
-rw-r--r--compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs9
-rw-r--r--compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs28
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_freebsd.rs4
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_haiku.rs2
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs3
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/i686_unknown_openbsd.rs3
-rw-r--r--compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs9
-rw-r--r--compiler/rustc_target/src/spec/i686_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/illumos_base.rs11
-rw-r--r--compiler/rustc_target/src/spec/mipsel_sony_psp.rs6
-rw-r--r--compiler/rustc_target/src/spec/mod.rs38
-rw-r--r--compiler/rustc_target/src/spec/msvc_base.rs13
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs6
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs3
-rw-r--r--compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs3
-rw-r--r--compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs2
-rw-r--r--compiler/rustc_target/src/spec/tests/tests_impl.rs97
-rw-r--r--compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs9
-rw-r--r--compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs2
-rw-r--r--compiler/rustc_target/src/spec/uefi_msvc_base.rs40
-rw-r--r--compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs23
-rw-r--r--compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs45
-rw-r--r--compiler/rustc_target/src/spec/wasm32_wasi.rs6
-rw-r--r--compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs30
-rw-r--r--compiler/rustc_target/src/spec/wasm_base.rs105
-rw-r--r--compiler/rustc_target/src/spec/windows_gnu_base.rs82
-rw-r--r--compiler/rustc_target/src/spec/windows_gnullvm_base.rs33
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs31
-rw-r--r--compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs9
-rw-r--r--compiler/rustc_target/src/spec/x86_64_apple_darwin.rs3
-rw-r--r--compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs73
-rw-r--r--compiler/rustc_target/src/spec/x86_64_linux_android.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_solaris.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs9
-rw-r--r--compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs3
-rw-r--r--compiler/rustc_target/src/spec/x86_64_sun_solaris.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_unknown_redox.rs2
-rw-r--r--compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs9
-rw-r--r--compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs2
-rw-r--r--compiler/rustc_typeck/src/check/wfcheck.rs19
-rw-r--r--compiler/rustc_typeck/src/coherence/mod.rs19
-rw-r--r--compiler/rustc_typeck/src/coherence/orphan.rs176
-rw-r--r--compiler/rustc_typeck/src/coherence/unsafety.rs35
-rw-r--r--compiler/rustc_typeck/src/lib.rs10
-rw-r--r--src/bootstrap/native.rs2
-rw-r--r--src/test/ui/chalkify/bugs/async.rs2
-rw-r--r--src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr24
-rw-r--r--src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr24
-rw-r--r--src/test/ui/coherence/coherence-impls-copy.stderr42
-rw-r--r--src/test/ui/coherence/coherence-impls-send.stderr22
-rw-r--r--src/test/ui/coherence/coherence-impls-sized.stderr66
-rw-r--r--src/test/ui/coherence/coherence-with-closure.stderr18
-rw-r--r--src/test/ui/coherence/coherence-with-generator.stderr18
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-80626.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-80626.stderr13
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-86218.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-87735.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-87748.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-87755.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-87803.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-88382.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-88460.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-88526.rs2
-rw-r--r--src/test/ui/generic-associated-types/bugs/issue-89008.rs2
-rw-r--r--src/test/ui/hrtb/issue-95034.rs2
-rw-r--r--src/test/ui/impl-trait/auto-trait.stderr18
-rw-r--r--src/test/ui/impl-trait/negative-reasoning.stderr22
-rw-r--r--src/test/ui/issues/issue-47511.rs2
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs32
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr126
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs32
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr126
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs32
-rw-r--r--src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr126
-rw-r--r--src/test/ui/trait-bounds/select-param-env-instead-of-blanket.rs2
-rw-r--r--src/test/ui/traits/alias/issue-83613.stderr16
-rw-r--r--src/test/ui/traits/issue-78372.rs1
-rw-r--r--src/test/ui/traits/issue-78372.stderr15
-rw-r--r--src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs2
-rw-r--r--src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs2
-rw-r--r--src/test/ui/union/issue-81199.stderr20
-rw-r--r--src/test/ui/wf/issue-96810.rs12
-rw-r--r--src/test/ui/wf/issue-96810.stderr19
-rw-r--r--src/tools/compiletest/src/header.rs24
125 files changed, 1113 insertions, 1089 deletions
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index 2a2dc6822ce..5579e4d19cf 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -4,7 +4,6 @@ use rustc_attr as attr;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
 use rustc_hir::{is_range_literal, Expr, ExprKind, Node};
 use rustc_middle::ty::layout::{IntegerExt, LayoutOf, SizeSkeleton};
 use rustc_middle::ty::subst::SubstsRef;
@@ -1483,39 +1482,32 @@ impl InvalidAtomicOrdering {
         None
     }
 
-    fn matches_ordering(cx: &LateContext<'_>, did: DefId, orderings: &[Symbol]) -> bool {
+    fn match_ordering(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<Symbol> {
+        let ExprKind::Path(ref ord_qpath) = ord_arg.kind else { return None };
+        let did = cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()?;
         let tcx = cx.tcx;
         let atomic_ordering = tcx.get_diagnostic_item(sym::Ordering);
-        orderings.iter().any(|ordering| {
-            tcx.item_name(did) == *ordering && {
-                let parent = tcx.parent(did);
-                Some(parent) == atomic_ordering
-                    // needed in case this is a ctor, not a variant
-                    || tcx.opt_parent(parent) == atomic_ordering
-            }
-        })
-    }
-
-    fn opt_ordering_defid(cx: &LateContext<'_>, ord_arg: &Expr<'_>) -> Option<DefId> {
-        if let ExprKind::Path(ref ord_qpath) = ord_arg.kind {
-            cx.qpath_res(ord_qpath, ord_arg.hir_id).opt_def_id()
-        } else {
-            None
-        }
+        let name = tcx.item_name(did);
+        let parent = tcx.parent(did);
+        [sym::Relaxed, sym::Release, sym::Acquire, sym::AcqRel, sym::SeqCst].into_iter().find(
+            |&ordering| {
+                name == ordering
+                    && (Some(parent) == atomic_ordering
+                            // needed in case this is a ctor, not a variant
+                            || tcx.opt_parent(parent) == atomic_ordering)
+            },
+        )
     }
 
     fn check_atomic_load_store(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        use rustc_hir::def::{DefKind, Res};
-        use rustc_hir::QPath;
         if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::load, sym::store])
             && let Some((ordering_arg, invalid_ordering)) = match method {
                 sym::load => Some((&args[1], sym::Release)),
                 sym::store => Some((&args[2], sym::Acquire)),
                 _ => None,
             }
-            && let ExprKind::Path(QPath::Resolved(_, path)) = ordering_arg.kind
-            && let Res::Def(DefKind::Ctor(..), ctor_id) = path.res
-            && Self::matches_ordering(cx, ctor_id, &[invalid_ordering, sym::AcqRel])
+            && let Some(ordering) = Self::match_ordering(cx, ordering_arg)
+            && (ordering == invalid_ordering || ordering == sym::AcqRel)
         {
             cx.struct_span_lint(INVALID_ATOMIC_ORDERING, ordering_arg.span, |diag| {
                 if method == sym::load {
@@ -1537,9 +1529,7 @@ impl InvalidAtomicOrdering {
             && let ExprKind::Path(ref func_qpath) = func.kind
             && let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id()
             && matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::fence | sym::compiler_fence))
-            && let ExprKind::Path(ref ordering_qpath) = &args[0].kind
-            && let Some(ordering_def_id) = cx.qpath_res(ordering_qpath, args[0].hir_id).opt_def_id()
-            && Self::matches_ordering(cx, ordering_def_id, &[sym::Relaxed])
+            && Self::match_ordering(cx, &args[0]) == Some(sym::Relaxed)
         {
             cx.struct_span_lint(INVALID_ATOMIC_ORDERING, args[0].span, |diag| {
                 diag.build("memory fences cannot have `Relaxed` ordering")
@@ -1550,62 +1540,56 @@ impl InvalidAtomicOrdering {
     }
 
     fn check_atomic_compare_exchange(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
-            && let Some((success_order_arg, failure_order_arg)) = match method {
-                sym::fetch_update => Some((&args[1], &args[2])),
-                sym::compare_exchange | sym::compare_exchange_weak => Some((&args[3], &args[4])),
-                _ => None,
-            }
-            && let Some(fail_ordering_def_id) = Self::opt_ordering_defid(cx, failure_order_arg)
-        {
-            // Helper type holding on to some checking and error reporting data. Has
-            // - (success ordering,
-            // - list of failure orderings forbidden by the success order,
-            // - suggestion message)
-            type OrdLintInfo = (Symbol, &'static [Symbol], &'static str);
-            const RELAXED: OrdLintInfo = (sym::Relaxed, &[sym::SeqCst, sym::Acquire], "ordering mode `Relaxed`");
-            const ACQUIRE: OrdLintInfo = (sym::Acquire, &[sym::SeqCst], "ordering modes `Acquire` or `Relaxed`");
-            const SEQ_CST: OrdLintInfo = (sym::SeqCst, &[], "ordering modes `Acquire`, `SeqCst` or `Relaxed`");
-            const RELEASE: OrdLintInfo = (sym::Release, RELAXED.1, RELAXED.2);
-            const ACQREL: OrdLintInfo = (sym::AcqRel, ACQUIRE.1, ACQUIRE.2);
-            const SEARCH: [OrdLintInfo; 5] = [RELAXED, ACQUIRE, SEQ_CST, RELEASE, ACQREL];
-
-            let success_lint_info = Self::opt_ordering_defid(cx, success_order_arg)
-                .and_then(|success_ord_def_id| -> Option<OrdLintInfo> {
-                    SEARCH
-                        .iter()
-                        .copied()
-                        .find(|(ordering, ..)| {
-                            Self::matches_ordering(cx, success_ord_def_id, &[*ordering])
-                        })
-                });
-            if Self::matches_ordering(cx, fail_ordering_def_id, &[sym::Release, sym::AcqRel]) {
-                // If we don't know the success order is, use what we'd suggest
-                // if it were maximally permissive.
-                let suggested = success_lint_info.unwrap_or(SEQ_CST).2;
-                cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
-                    let msg = format!(
-                        "{}'s failure ordering may not be `Release` or `AcqRel`",
-                        method,
-                    );
-                    diag.build(&msg)
-                        .help(&format!("consider using {} instead", suggested))
-                        .emit();
-                });
-            } else if let Some((success_ord, bad_ords_given_success, suggested)) = success_lint_info {
-                if Self::matches_ordering(cx, fail_ordering_def_id, bad_ords_given_success) {
-                    cx.struct_span_lint(INVALID_ATOMIC_ORDERING, failure_order_arg.span, |diag| {
-                        let msg = format!(
-                            "{}'s failure ordering may not be stronger than the success ordering of `{}`",
-                            method,
-                            success_ord,
-                        );
-                        diag.build(&msg)
-                            .help(&format!("consider using {} instead", suggested))
-                            .emit();
-                    });
-                }
-            }
+        let Some((method, args)) = Self::inherent_atomic_method_call(cx, expr, &[sym::fetch_update, sym::compare_exchange, sym::compare_exchange_weak])
+            else {return };
+
+        let (success_order_arg, fail_order_arg) = match method {
+            sym::fetch_update => (&args[1], &args[2]),
+            sym::compare_exchange | sym::compare_exchange_weak => (&args[3], &args[4]),
+            _ => return,
+        };
+
+        let Some(fail_ordering) = Self::match_ordering(cx, fail_order_arg) else { return };
+
+        if matches!(fail_ordering, sym::Release | sym::AcqRel) {
+            cx.struct_span_lint(INVALID_ATOMIC_ORDERING, fail_order_arg.span, |diag| {
+                diag.build(&format!(
+                    "`{method}`'s failure ordering may not be `Release` or `AcqRel`, \
+                    since a failed `{method}` does not result in a write",
+                ))
+                .span_label(fail_order_arg.span, "invalid failure ordering")
+                .help("consider using `Acquire` or `Relaxed` failure ordering instead")
+                .emit();
+            });
+        }
+
+        let Some(success_ordering) = Self::match_ordering(cx, success_order_arg) else { return };
+
+        if matches!(
+            (success_ordering, fail_ordering),
+            (sym::Relaxed | sym::Release, sym::Acquire)
+                | (sym::Relaxed | sym::Release | sym::Acquire | sym::AcqRel, sym::SeqCst)
+        ) {
+            let success_suggestion =
+                if success_ordering == sym::Release && fail_ordering == sym::Acquire {
+                    sym::AcqRel
+                } else {
+                    fail_ordering
+                };
+            cx.struct_span_lint(INVALID_ATOMIC_ORDERING, success_order_arg.span, |diag| {
+                diag.build(&format!(
+                    "`{method}`'s success ordering must be at least as strong as its failure ordering"
+                ))
+                .span_label(fail_order_arg.span, format!("`{fail_ordering}` failure ordering"))
+                .span_label(success_order_arg.span, format!("`{success_ordering}` success ordering"))
+                .span_suggestion_short(
+                    success_order_arg.span,
+                    format!("consider using `{success_suggestion}` success ordering instead"),
+                    format!("std::sync::atomic::Ordering::{success_suggestion}"),
+                    Applicability::MaybeIncorrect,
+                )
+                .emit();
+            });
         }
     }
 }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index ca2c03cb614..6c3894bbcf7 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -906,9 +906,10 @@ rustc_queries! {
 
     /// Checks whether all impls in the crate pass the overlap check, returning
     /// which impls fail it. If all impls are correct, the returned slice is empty.
-    query orphan_check_crate(_: ()) -> &'tcx [LocalDefId] {
-        desc {
-            "checking whether the immpl in the this crate follow the orphan rules",
+    query orphan_check_impl(key: LocalDefId) -> Result<(), ErrorGuaranteed> {
+        desc { |tcx|
+            "checking whether impl `{}` follows the orphan rules",
+            tcx.def_path_str(key.to_def_id()),
         }
     }
 
diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 5258d37a14c..94ec89776a9 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -406,7 +406,7 @@ pub enum ObligationCauseCode<'tcx> {
     QuestionMark,
 
     /// Well-formed checking. If a `WellFormedLoc` is provided,
-    /// then it will be used to eprform HIR-based wf checking
+    /// then it will be used to perform HIR-based wf checking
     /// after an error occurs, in order to generate a more precise error span.
     /// This is purely for diagnostic purposes - it is always
     /// correct to use `MiscObligation` instead, or to specify
diff --git a/compiler/rustc_target/src/lib.rs b/compiler/rustc_target/src/lib.rs
index a8ddcc9bfac..59dbea70534 100644
--- a/compiler/rustc_target/src/lib.rs
+++ b/compiler/rustc_target/src/lib.rs
@@ -8,6 +8,7 @@
 //! LLVM.
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(assert_matches)]
 #![feature(associated_type_bounds)]
 #![feature(exhaustive_patterns)]
 #![feature(let_else)]
diff --git a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
index 86f76fdb6a7..9d36e37d7b8 100644
--- a/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/aarch64_apple_darwin.rs
@@ -8,7 +8,7 @@ pub fn target() -> Target {
     // FIXME: The leak sanitizer currently fails the tests, see #88132.
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI | SanitizerSet::THREAD;
 
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-arch".into(), "arm64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-arch", "arm64"]);
     base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
 
     // Clang automatically chooses a more specific target based on
diff --git a/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs
index 965b254c289..162b091b269 100644
--- a/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs
+++ b/compiler/rustc_target/src/spec/aarch64_unknown_uefi.rs
@@ -2,20 +2,13 @@
 // uefi-base module for generic UEFI options.
 
 use super::uefi_msvc_base;
-use crate::spec::{LinkerFlavor, LldFlavor, Target};
+use crate::spec::{LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = uefi_msvc_base::opts();
 
     base.max_atomic_width = Some(64);
-
-    let pre_link_args_msvc = vec!["/machine:arm64".into()];
-
-    base.pre_link_args.get_mut(&LinkerFlavor::Msvc).unwrap().extend(pre_link_args_msvc.clone());
-    base.pre_link_args
-        .get_mut(&LinkerFlavor::Lld(LldFlavor::Link))
-        .unwrap()
-        .extend(pre_link_args_msvc);
+    base.add_pre_link_args(LinkerFlavor::Msvc, &["/machine:arm64"]);
 
     Target {
         llvm_target: "aarch64-unknown-windows".into(),
diff --git a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
index 67df73fa935..8c2a9bcfde6 100644
--- a/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
+++ b/compiler/rustc_target/src/spec/armv6k_nintendo_3ds.rs
@@ -1,19 +1,13 @@
-use crate::spec::{cvs, LinkArgs, LinkerFlavor, RelocModel, Target, TargetOptions};
+use crate::spec::{cvs, LinkerFlavor, RelocModel, Target, TargetOptions};
 
 /// A base target for Nintendo 3DS devices using the devkitARM toolchain.
 ///
 /// Requires the devkitARM toolchain for 3DS targets on the host system.
 
 pub fn target() -> Target {
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args.insert(
+    let pre_link_args = TargetOptions::link_args(
         LinkerFlavor::Gcc,
-        vec![
-            "-specs=3dsx.specs".into(),
-            "-mtune=mpcore".into(),
-            "-mfloat-abi=hard".into(),
-            "-mtp=soft".into(),
-        ],
+        &["-specs=3dsx.specs", "-mtune=mpcore", "-mfloat-abi=hard", "-mtp=soft"],
     );
 
     Target {
diff --git a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs
index 2afd93fcad8..38c117a495e 100644
--- a/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs
+++ b/compiler/rustc_target/src/spec/armv7_linux_androideabi.rs
@@ -10,7 +10,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::android_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]);
     Target {
         llvm_target: "armv7-none-linux-android".into(),
         pointer_width: 32,
diff --git a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
index 269bf8b8bcd..b4cf2c5ee22 100644
--- a/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/asmjs_unknown_emscripten.rs
@@ -2,10 +2,6 @@ use super::{wasm32_unknown_emscripten, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut target = wasm32_unknown_emscripten::target();
-    target.post_link_args.entry(LinkerFlavor::Em).or_default().extend(vec![
-        "-sWASM=0".into(),
-        "--memory-init-file".into(),
-        "0".into(),
-    ]);
+    target.add_post_link_args(LinkerFlavor::Em, &["-sWASM=0", "--memory-init-file", "0"]);
     target
 }
diff --git a/compiler/rustc_target/src/spec/avr_gnu_base.rs b/compiler/rustc_target/src/spec/avr_gnu_base.rs
index c288e8b0e9e..4fd6c06394d 100644
--- a/compiler/rustc_target/src/spec/avr_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/avr_gnu_base.rs
@@ -3,7 +3,8 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 /// A base target for AVR devices using the GNU toolchain.
 ///
 /// Requires GNU avr-gcc and avr-binutils on the host system.
-pub fn target(target_cpu: &'static str) -> Target {
+/// FIXME: Remove the second parameter when const string concatenation is possible.
+pub fn target(target_cpu: &'static str, mmcu: &'static str) -> Target {
     Target {
         arch: "avr".into(),
         data_layout: "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8".into(),
@@ -17,10 +18,8 @@ pub fn target(target_cpu: &'static str) -> Target {
             linker: Some("avr-gcc".into()),
             executables: true,
             eh_frame_header: false,
-            pre_link_args: [(LinkerFlavor::Gcc, vec![format!("-mmcu={}", target_cpu).into()])]
-                .into_iter()
-                .collect(),
-            late_link_args: [(LinkerFlavor::Gcc, vec!["-lgcc".into()])].into_iter().collect(),
+            pre_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &[mmcu]),
+            late_link_args: TargetOptions::link_args(LinkerFlavor::Gcc, &["-lgcc"]),
             max_atomic_width: Some(0),
             atomic_cas: false,
             ..TargetOptions::default()
diff --git a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs
index 6871ca0f789..6c16b03cc28 100644
--- a/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs
+++ b/compiler/rustc_target/src/spec/avr_unknown_gnu_atmega328.rs
@@ -1,5 +1,5 @@
 use crate::spec::Target;
 
 pub fn target() -> Target {
-    super::avr_gnu_base::target("atmega328")
+    super::avr_gnu_base::target("atmega328", "-mmcu=atmega328")
 }
diff --git a/compiler/rustc_target/src/spec/fuchsia_base.rs b/compiler/rustc_target/src/spec/fuchsia_base.rs
index b64875e32bd..b02b70f76ee 100644
--- a/compiler/rustc_target/src/spec/fuchsia_base.rs
+++ b/compiler/rustc_target/src/spec/fuchsia_base.rs
@@ -1,23 +1,20 @@
-use crate::spec::{
-    crt_objects, cvs, LinkArgs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions,
-};
+use crate::spec::{crt_objects, cvs, LinkOutputKind, LinkerFlavor, LldFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args.insert(
-        LinkerFlavor::Lld(LldFlavor::Ld),
-        vec![
-            "--build-id".into(),
-            "--hash-style=gnu".into(),
-            "-z".into(),
-            "max-page-size=4096".into(),
-            "-z".into(),
-            "now".into(),
-            "-z".into(),
-            "rodynamic".into(),
-            "-z".into(),
-            "separate-loadable-segments".into(),
-            "--pack-dyn-relocs=relr".into(),
+    let pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Ld,
+        &[
+            "--build-id",
+            "--hash-style=gnu",
+            "-z",
+            "max-page-size=4096",
+            "-z",
+            "now",
+            "-z",
+            "rodynamic",
+            "-z",
+            "separate-loadable-segments",
+            "--pack-dyn-relocs=relr",
         ],
     );
 
diff --git a/compiler/rustc_target/src/spec/hermit_base.rs b/compiler/rustc_target/src/spec/hermit_base.rs
index 7cbd42417e6..e43153177f0 100644
--- a/compiler/rustc_target/src/spec/hermit_base.rs
+++ b/compiler/rustc_target/src/spec/hermit_base.rs
@@ -1,10 +1,9 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel};
+use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, TargetOptions, TlsModel};
 
 pub fn opts() -> TargetOptions {
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args.insert(
-        LinkerFlavor::Lld(LldFlavor::Ld),
-        vec!["--build-id".into(), "--hash-style=gnu".into(), "--Bstatic".into()],
+    let pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Ld,
+        &["--build-id", "--hash-style=gnu", "--Bstatic"],
     );
 
     TargetOptions {
diff --git a/compiler/rustc_target/src/spec/i686_apple_darwin.rs b/compiler/rustc_target/src/spec/i686_apple_darwin.rs
index ad716a6cd5a..1718bd77b86 100644
--- a/compiler/rustc_target/src/spec/i686_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/i686_apple_darwin.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::apple_base::opts("macos");
     base.cpu = "yonah".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs
index 554b0f34499..6318654399c 100644
--- a/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs
+++ b/compiler/rustc_target/src/spec/i686_pc_windows_gnu.rs
@@ -1,19 +1,16 @@
-use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target};
+use crate::spec::{FramePointer, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = super::windows_gnu_base::opts();
     base.cpu = "pentium4".into();
-    base.pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pe".into()]);
     base.max_atomic_width = Some(64);
     base.frame_pointer = FramePointer::Always; // Required for backtraces
     base.linker = Some("i686-w64-mingw32-gcc".into());
 
     // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
     // space available to x86 Windows binaries on x86_64.
-    base.pre_link_args
-        .entry(LinkerFlavor::Gcc)
-        .or_default()
-        .push("-Wl,--large-address-aware".into());
+    base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]);
 
     Target {
         llvm_target: "i686-pc-windows-gnu".into(),
diff --git a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs
index fb0cb6a6943..f4ceaa1ca4b 100644
--- a/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/i686_pc_windows_msvc.rs
@@ -1,24 +1,22 @@
-use crate::spec::{LinkerFlavor, LldFlavor, Target};
+use crate::spec::{LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = super::windows_msvc_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
 
-    let pre_link_args_msvc = vec![
-        // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
-        // space available to x86 Windows binaries on x86_64.
-        "/LARGEADDRESSAWARE".into(),
-        // Ensure the linker will only produce an image if it can also produce a table of
-        // the image's safe exception handlers.
-        // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers
-        "/SAFESEH".into(),
-    ];
-    base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone());
-    base.pre_link_args
-        .entry(LinkerFlavor::Lld(LldFlavor::Link))
-        .or_default()
-        .extend(pre_link_args_msvc);
+    base.add_pre_link_args(
+        LinkerFlavor::Msvc,
+        &[
+            // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
+            // space available to x86 Windows binaries on x86_64.
+            "/LARGEADDRESSAWARE",
+            // Ensure the linker will only produce an image if it can also produce a table of
+            // the image's safe exception handlers.
+            // https://docs.microsoft.com/en-us/cpp/build/reference/safeseh-image-has-safe-exception-handlers
+            "/SAFESEH",
+        ],
+    );
     // Workaround for #95429
     base.has_thread_local = false;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
index 9f0cb04c65d..aff284bf2bc 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_freebsd.rs
@@ -4,9 +4,7 @@ pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    let pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-    pre_link_args.push("-m32".into());
-    pre_link_args.push("-Wl,-znotext".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-znotext"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs
index d1af163f1cf..87aa74e406c 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_haiku.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_haiku.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::haiku_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m32".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
index 0998c618f31..765803d1692 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_linux_gnu.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
index a697f292da0..d9492804349 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_linux_musl.rs
@@ -4,8 +4,7 @@ pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-Wl,-melf_i386".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-Wl,-melf_i386"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
index 2807d328205..8de698b51f0 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_netbsd.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::netbsd_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
index 78462eb63b8..7f25a1a16c1 100644
--- a/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/i686_unknown_openbsd.rs
@@ -4,8 +4,7 @@ pub fn target() -> Target {
     let mut base = super::openbsd_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-fuse-ld=lld".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "-fuse-ld=lld"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs
index 75f7a2209c8..d52810d2fb0 100644
--- a/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs
+++ b/compiler/rustc_target/src/spec/i686_uwp_windows_gnu.rs
@@ -1,18 +1,15 @@
-use crate::spec::{FramePointer, LinkerFlavor, LldFlavor, Target};
+use crate::spec::{FramePointer, LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = super::windows_uwp_gnu_base::opts();
     base.cpu = "pentium4".into();
-    base.pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pe".into()]);
     base.max_atomic_width = Some(64);
     base.frame_pointer = FramePointer::Always; // Required for backtraces
 
     // Mark all dynamic libraries and executables as compatible with the larger 4GiB address
     // space available to x86 Windows binaries on x86_64.
-    base.pre_link_args
-        .entry(LinkerFlavor::Gcc)
-        .or_default()
-        .push("-Wl,--large-address-aware".into());
+    base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pe", "--large-address-aware"]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-Wl,--large-address-aware"]);
 
     Target {
         llvm_target: "i686-pc-windows-gnu".into(),
diff --git a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
index d51ed7c1f7a..f62404e8279 100644
--- a/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/i686_wrs_vxworks.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::vxworks_base::opts();
     base.cpu = "pentium4".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/illumos_base.rs b/compiler/rustc_target/src/spec/illumos_base.rs
index ef8f90a4da8..b0e1b109be1 100644
--- a/compiler/rustc_target/src/spec/illumos_base.rs
+++ b/compiler/rustc_target/src/spec/illumos_base.rs
@@ -1,10 +1,9 @@
-use crate::spec::{cvs, FramePointer, LinkArgs, LinkerFlavor, TargetOptions};
+use crate::spec::{cvs, FramePointer, LinkerFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
-    let mut late_link_args = LinkArgs::new();
-    late_link_args.insert(
+    let late_link_args = TargetOptions::link_args(
         LinkerFlavor::Gcc,
-        vec![
+        &[
             // The illumos libc contains a stack unwinding implementation, as
             // does libgcc_s.  The latter implementation includes several
             // additional symbols that are not always in base libc.  To force
@@ -15,13 +14,13 @@ pub fn opts() -> TargetOptions {
             // FIXME: This should be replaced by a more complete and generic
             // mechanism for controlling the order of library arguments passed
             // to the linker.
-            "-lc".into(),
+            "-lc",
             // LLVM will insert calls to the stack protector functions
             // "__stack_chk_fail" and "__stack_chk_guard" into code in native
             // object files.  Some platforms include these symbols directly in
             // libc, but at least historically these have been provided in
             // libssp.so on illumos and Solaris systems.
-            "-lssp".into(),
+            "-lssp",
         ],
     );
 
diff --git a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
index 03e0934ea5e..e3522de6de0 100644
--- a/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
+++ b/compiler/rustc_target/src/spec/mipsel_sony_psp.rs
@@ -1,13 +1,11 @@
 use crate::spec::{cvs, Target, TargetOptions};
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, RelocModel};
+use crate::spec::{LinkerFlavor, LldFlavor, RelocModel};
 
 // The PSP has custom linker requirements.
 const LINKER_SCRIPT: &str = include_str!("./mipsel_sony_psp_linker_script.ld");
 
 pub fn target() -> Target {
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args
-        .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["--emit-relocs".into(), "--nmagic".into()]);
+    let pre_link_args = TargetOptions::link_args(LinkerFlavor::Ld, &["--emit-relocs", "--nmagic"]);
 
     Target {
         llvm_target: "mipsel-sony-psp".into(),
diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index da0589cdd20..a08603da040 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -1459,6 +1459,44 @@ pub struct TargetOptions {
     pub supports_stack_protector: bool,
 }
 
+/// Add arguments for the given flavor and also for its "twin" flavors
+/// that have a compatible command line interface.
+fn add_link_args(link_args: &mut LinkArgs, flavor: LinkerFlavor, args: &[&'static str]) {
+    let mut insert = |flavor| {
+        link_args.entry(flavor).or_default().extend(args.iter().copied().map(Cow::Borrowed))
+    };
+    insert(flavor);
+    match flavor {
+        LinkerFlavor::Ld => insert(LinkerFlavor::Lld(LldFlavor::Ld)),
+        LinkerFlavor::Msvc => insert(LinkerFlavor::Lld(LldFlavor::Link)),
+        LinkerFlavor::Lld(LldFlavor::Wasm) => {}
+        LinkerFlavor::Lld(lld_flavor) => {
+            panic!("add_link_args: use non-LLD flavor for {:?}", lld_flavor)
+        }
+        LinkerFlavor::Gcc
+        | LinkerFlavor::Em
+        | LinkerFlavor::L4Bender
+        | LinkerFlavor::BpfLinker
+        | LinkerFlavor::PtxLinker => {}
+    }
+}
+
+impl TargetOptions {
+    fn link_args(flavor: LinkerFlavor, args: &[&'static str]) -> LinkArgs {
+        let mut link_args = LinkArgs::new();
+        add_link_args(&mut link_args, flavor, args);
+        link_args
+    }
+
+    fn add_pre_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
+        add_link_args(&mut self.pre_link_args, flavor, args);
+    }
+
+    fn add_post_link_args(&mut self, flavor: LinkerFlavor, args: &[&'static str]) {
+        add_link_args(&mut self.post_link_args, flavor, args);
+    }
+}
+
 impl Default for TargetOptions {
     /// Creates a set of "sane defaults" for any target. This is still
     /// incomplete, and if used for compilation, will certainly not work.
diff --git a/compiler/rustc_target/src/spec/msvc_base.rs b/compiler/rustc_target/src/spec/msvc_base.rs
index 00cc9620243..c4df4b546e3 100644
--- a/compiler/rustc_target/src/spec/msvc_base.rs
+++ b/compiler/rustc_target/src/spec/msvc_base.rs
@@ -1,14 +1,9 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
+use crate::spec::{LinkerFlavor, LldFlavor, SplitDebuginfo, TargetOptions};
 
 pub fn opts() -> TargetOptions {
-    let pre_link_args_msvc = vec![
-        // Suppress the verbose logo and authorship debugging output, which would needlessly
-        // clog any log files.
-        "/NOLOGO".into(),
-    ];
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args.insert(LinkerFlavor::Msvc, pre_link_args_msvc.clone());
-    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Link), pre_link_args_msvc);
+    // Suppress the verbose logo and authorship debugging output, which would needlessly
+    // clog any log files.
+    let pre_link_args = TargetOptions::link_args(LinkerFlavor::Msvc, &["/NOLOGO"]);
 
     TargetOptions {
         linker_flavor: LinkerFlavor::Msvc,
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs
index 595769c4bfa..803453c4ac4 100644
--- a/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_freebsd.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
     base.cpu = "ppc64".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs
index 24d5d187e1a..5413c4f33ff 100644
--- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_gnu.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, RelroLevel, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
     base.cpu = "ppc64".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     // ld.so in at least RHEL6 on ppc64 has a bug related to BIND_NOW, so only enable partial RELRO
diff --git a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs
index 0f465ccfe77..159335eb607 100644
--- a/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/powerpc64_unknown_linux_musl.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
     base.cpu = "ppc64".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs
index 491d344aedb..b7420d232ca 100644
--- a/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/powerpc64_wrs_vxworks.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::vxworks_base::opts();
     base.cpu = "ppc64".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs
index b198e667ccc..a3d18004371 100644
--- a/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_freebsd.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
     base.cpu = "ppc64le".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs
index 09e3936db26..e18ff3be448 100644
--- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_gnu.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
     base.cpu = "ppc64le".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs
index 8a947b091cb..b84943d23a9 100644
--- a/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/powerpc64le_unknown_linux_musl.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
     base.cpu = "ppc64le".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
index c27b84775df..516b2de37ea 100644
--- a/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_freebsd.rs
@@ -3,12 +3,8 @@ use crate::spec::{LinkerFlavor, RelocModel, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
     // Extra hint to linker that we are generating secure-PLT code.
-    base.pre_link_args
-        .entry(LinkerFlavor::Gcc)
-        .or_default()
-        .push("--target=powerpc-unknown-freebsd13.0".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--target=powerpc-unknown-freebsd13.0"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs
index 88f61500e3c..6686a0bbf04 100644
--- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnu.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs
index 3ee548750b9..6a250f4b51c 100644
--- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_gnuspe.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs
index ce33c787f33..34200c67906 100644
--- a/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_linux_musl.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs
index 998225f4dae..60661ef9b5d 100644
--- a/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/powerpc_unknown_netbsd.rs
@@ -3,7 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::netbsd_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs
index 76709cec591..3f24966e06e 100644
--- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks.rs
@@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::vxworks_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m32".into());
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m32", "--secure-plt"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs
index 7b5d1242c52..0f04f41f9e5 100644
--- a/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs
+++ b/compiler/rustc_target/src/spec/powerpc_wrs_vxworks_spe.rs
@@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::vxworks_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mspe".into());
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("--secure-plt".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-mspe", "--secure-plt"]);
     base.max_atomic_width = Some(32);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs
index 718303a4b4d..836ab0e3728 100644
--- a/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/sparc64_unknown_netbsd.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 pub fn target() -> Target {
     let mut base = super::netbsd_base::opts();
     base.cpu = "v9".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs
index 2aaa0ca6df8..4a192df392f 100644
--- a/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/sparc64_unknown_openbsd.rs
@@ -5,7 +5,7 @@ pub fn target() -> Target {
     let mut base = super::openbsd_base::opts();
     base.endian = Endian::Big;
     base.cpu = "v9".into();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs
index 71d3de0bfd1..ea4fafa4b06 100644
--- a/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/sparc_unknown_linux_gnu.rs
@@ -6,7 +6,7 @@ pub fn target() -> Target {
     base.endian = Endian::Big;
     base.cpu = "v9".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mv8plus".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-mv8plus"]);
 
     Target {
         llvm_target: "sparc-unknown-linux-gnu".into(),
diff --git a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs
index 79ae54aa666..aac09181a74 100644
--- a/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs
+++ b/compiler/rustc_target/src/spec/sparcv9_sun_solaris.rs
@@ -4,7 +4,7 @@ use crate::spec::{LinkerFlavor, Target};
 pub fn target() -> Target {
     let mut base = super::solaris_base::opts();
     base.endian = Endian::Big;
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // llvm calls this "v9"
     base.cpu = "v9".into();
     base.vendor = "sun".into();
diff --git a/compiler/rustc_target/src/spec/tests/tests_impl.rs b/compiler/rustc_target/src/spec/tests/tests_impl.rs
index 0865ca7ea7d..c7c5a231901 100644
--- a/compiler/rustc_target/src/spec/tests/tests_impl.rs
+++ b/compiler/rustc_target/src/spec/tests/tests_impl.rs
@@ -1,4 +1,5 @@
 use super::super::*;
+use std::assert_matches::assert_matches;
 
 // Test target self-consistency and JSON encoding/decoding roundtrip.
 pub(super) fn test_target(target: Target) {
@@ -14,35 +15,105 @@ impl Target {
         assert_eq!(self.is_like_wasm, self.arch == "wasm32" || self.arch == "wasm64");
         assert!(self.is_like_windows || !self.is_like_msvc);
 
-        // Check that LLD with the given flavor is treated identically to the linker it emulates.
-        // If your target really needs to deviate from the rules below, except it and document the
-        // reasons.
-        assert_eq!(
-            self.linker_flavor == LinkerFlavor::Msvc
-                || self.linker_flavor == LinkerFlavor::Lld(LldFlavor::Link),
-            self.lld_flavor == LldFlavor::Link,
-        );
-        assert_eq!(self.is_like_msvc, self.lld_flavor == LldFlavor::Link);
-        for args in &[
+        // Check that default linker flavor and lld flavor are compatible
+        // with some other key properties.
+        assert_eq!(self.is_like_osx, matches!(self.lld_flavor, LldFlavor::Ld64));
+        assert_eq!(self.is_like_msvc, matches!(self.lld_flavor, LldFlavor::Link));
+        assert_eq!(self.is_like_wasm, matches!(self.lld_flavor, LldFlavor::Wasm));
+        assert_eq!(self.os == "l4re", matches!(self.linker_flavor, LinkerFlavor::L4Bender));
+        assert_eq!(self.os == "emscripten", matches!(self.linker_flavor, LinkerFlavor::Em));
+        assert_eq!(self.arch == "bpf", matches!(self.linker_flavor, LinkerFlavor::BpfLinker));
+        assert_eq!(self.arch == "nvptx64", matches!(self.linker_flavor, LinkerFlavor::PtxLinker));
+
+        for args in [
             &self.pre_link_args,
             &self.late_link_args,
             &self.late_link_args_dynamic,
             &self.late_link_args_static,
             &self.post_link_args,
         ] {
+            for (&flavor, flavor_args) in args {
+                assert!(!flavor_args.is_empty());
+                // Check that flavors mentioned in link args are compatible with the default flavor.
+                match (self.linker_flavor, self.lld_flavor) {
+                    (
+                        LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc,
+                        LldFlavor::Ld,
+                    ) => {
+                        assert_matches!(
+                            flavor,
+                            LinkerFlavor::Ld | LinkerFlavor::Lld(LldFlavor::Ld) | LinkerFlavor::Gcc
+                        )
+                    }
+                    (LinkerFlavor::Gcc, LldFlavor::Ld64) => {
+                        assert_matches!(flavor, LinkerFlavor::Gcc)
+                    }
+                    (LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link), LldFlavor::Link) => {
+                        assert_matches!(
+                            flavor,
+                            LinkerFlavor::Msvc | LinkerFlavor::Lld(LldFlavor::Link)
+                        )
+                    }
+                    (LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc, LldFlavor::Wasm) => {
+                        assert_matches!(
+                            flavor,
+                            LinkerFlavor::Lld(LldFlavor::Wasm) | LinkerFlavor::Gcc
+                        )
+                    }
+                    (LinkerFlavor::L4Bender, LldFlavor::Ld) => {
+                        assert_matches!(flavor, LinkerFlavor::L4Bender)
+                    }
+                    (LinkerFlavor::Em, LldFlavor::Wasm) => {
+                        assert_matches!(flavor, LinkerFlavor::Em)
+                    }
+                    (LinkerFlavor::BpfLinker, LldFlavor::Ld) => {
+                        assert_matches!(flavor, LinkerFlavor::BpfLinker)
+                    }
+                    (LinkerFlavor::PtxLinker, LldFlavor::Ld) => {
+                        assert_matches!(flavor, LinkerFlavor::PtxLinker)
+                    }
+                    flavors => unreachable!("unexpected flavor combination: {:?}", flavors),
+                }
+
+                // Check that link args for cc and non-cc versions of flavors are consistent.
+                let check_noncc = |noncc_flavor| {
+                    if let Some(noncc_args) = args.get(&noncc_flavor) {
+                        for arg in flavor_args {
+                            if let Some(suffix) = arg.strip_prefix("-Wl,") {
+                                assert!(noncc_args.iter().any(|a| a == suffix));
+                            }
+                        }
+                    }
+                };
+                match self.linker_flavor {
+                    LinkerFlavor::Gcc => match self.lld_flavor {
+                        LldFlavor::Ld => {
+                            check_noncc(LinkerFlavor::Ld);
+                            check_noncc(LinkerFlavor::Lld(LldFlavor::Ld));
+                        }
+                        LldFlavor::Wasm => check_noncc(LinkerFlavor::Lld(LldFlavor::Wasm)),
+                        LldFlavor::Ld64 | LldFlavor::Link => {}
+                    },
+                    _ => {}
+                }
+            }
+
+            // Check that link args for lld and non-lld versions of flavors are consistent.
+            assert_eq!(args.get(&LinkerFlavor::Ld), args.get(&LinkerFlavor::Lld(LldFlavor::Ld)));
             assert_eq!(
                 args.get(&LinkerFlavor::Msvc),
                 args.get(&LinkerFlavor::Lld(LldFlavor::Link)),
             );
-            if args.contains_key(&LinkerFlavor::Msvc) {
-                assert_eq!(self.lld_flavor, LldFlavor::Link);
-            }
         }
+
         assert!(
             (self.pre_link_objects_fallback.is_empty()
                 && self.post_link_objects_fallback.is_empty())
                 || self.crt_objects_fallback.is_some()
         );
+
+        // If your target really needs to deviate from the rules below,
+        // except it and document the reasons.
         // Keep the default "unknown" vendor instead.
         assert_ne!(self.vendor, "");
         if !self.can_use_os_unknown() {
diff --git a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs
index f6cbbd38cf4..4d09d3a4d10 100644
--- a/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs
+++ b/compiler/rustc_target/src/spec/thumbv7a_pc_windows_msvc.rs
@@ -1,4 +1,4 @@
-use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, Target, TargetOptions};
+use crate::spec::{LinkerFlavor, PanicStrategy, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::windows_msvc_base::opts();
@@ -9,12 +9,7 @@ pub fn target() -> Target {
     // should be smart enough to insert branch islands only
     // where necessary, but this is not the observed behavior.
     // Disabling the LBR optimization works around the issue.
-    let pre_link_args_msvc = "/OPT:NOLBR";
-    base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().push(pre_link_args_msvc.into());
-    base.pre_link_args
-        .entry(LinkerFlavor::Lld(LldFlavor::Link))
-        .or_default()
-        .push(pre_link_args_msvc.into());
+    base.add_pre_link_args(LinkerFlavor::Msvc, &["/OPT:NOLBR"]);
 
     Target {
         llvm_target: "thumbv7a-pc-windows-msvc".into(),
diff --git a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs
index 9a3e8b5c5f8..4cad9e18370 100644
--- a/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs
+++ b/compiler/rustc_target/src/spec/thumbv7neon_linux_androideabi.rs
@@ -10,7 +10,7 @@ use crate::spec::{LinkerFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
     let mut base = super::android_base::opts();
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-march=armv7-a".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-march=armv7-a"]);
     Target {
         llvm_target: "armv7-none-linux-android".into(),
         pointer_width: 32,
diff --git a/compiler/rustc_target/src/spec/uefi_msvc_base.rs b/compiler/rustc_target/src/spec/uefi_msvc_base.rs
index bc7244b3a45..aee8eb2e31c 100644
--- a/compiler/rustc_target/src/spec/uefi_msvc_base.rs
+++ b/compiler/rustc_target/src/spec/uefi_msvc_base.rs
@@ -14,27 +14,25 @@ use crate::spec::{LinkerFlavor, LldFlavor, PanicStrategy, StackProbeType, Target
 pub fn opts() -> TargetOptions {
     let mut base = super::msvc_base::opts();
 
-    let pre_link_args_msvc = vec![
-        // Non-standard subsystems have no default entry-point in PE+ files. We have to define
-        // one. "efi_main" seems to be a common choice amongst other implementations and the
-        // spec.
-        "/entry:efi_main".into(),
-        // COFF images have a "Subsystem" field in their header, which defines what kind of
-        // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
-        // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
-        // which is very likely the most common option. Individual projects can override this
-        // with custom linker flags.
-        // The subsystem-type only has minor effects on the application. It defines the memory
-        // regions the application is loaded into (runtime-drivers need to be put into
-        // reserved areas), as well as whether a return from the entry-point is treated as
-        // exit (default for applications).
-        "/subsystem:efi_application".into(),
-    ];
-    base.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone());
-    base.pre_link_args
-        .entry(LinkerFlavor::Lld(LldFlavor::Link))
-        .or_default()
-        .extend(pre_link_args_msvc);
+    base.add_pre_link_args(
+        LinkerFlavor::Msvc,
+        &[
+            // Non-standard subsystems have no default entry-point in PE+ files. We have to define
+            // one. "efi_main" seems to be a common choice amongst other implementations and the
+            // spec.
+            "/entry:efi_main",
+            // COFF images have a "Subsystem" field in their header, which defines what kind of
+            // program it is. UEFI has 3 fields reserved, which are EFI_APPLICATION,
+            // EFI_BOOT_SERVICE_DRIVER, and EFI_RUNTIME_DRIVER. We default to EFI_APPLICATION,
+            // which is very likely the most common option. Individual projects can override this
+            // with custom linker flags.
+            // The subsystem-type only has minor effects on the application. It defines the memory
+            // regions the application is loaded into (runtime-drivers need to be put into
+            // reserved areas), as well as whether a return from the entry-point is treated as
+            // exit (default for applications).
+            "/subsystem:efi_application",
+        ],
+    );
 
     TargetOptions {
         os: "uefi".into(),
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
index 1b94c59b55f..c7e7d221086 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_emscripten.rs
@@ -2,21 +2,11 @@ use super::{cvs, wasm_base};
 use super::{LinkArgs, LinkerFlavor, PanicStrategy, RelocModel, Target, TargetOptions};
 
 pub fn target() -> Target {
-    let mut options = wasm_base::options();
-
-    let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-
-    // Rust really needs a way for users to specify exports and imports in
-    // the source code. --export-dynamic isn't the right tool for this job,
-    // however it does have the side effect of automatically exporting a lot
-    // of symbols, which approximates what people want when compiling for
-    // wasm32-unknown-unknown expect, so use it for now.
-    clang_args.push("--export-dynamic".into());
-
-    let mut post_link_args = LinkArgs::new();
-    post_link_args.insert(
+    // Reset flags for non-Em flavors back to empty to satisfy sanity checking tests.
+    let pre_link_args = LinkArgs::new();
+    let post_link_args = TargetOptions::link_args(
         LinkerFlavor::Em,
-        vec!["-sABORTING_MALLOC=0".into(), "-Wl,--fatal-warnings".into()],
+        &["-sABORTING_MALLOC=0", "-Wl,--fatal-warnings"],
     );
 
     let opts = TargetOptions {
@@ -26,12 +16,13 @@ pub fn target() -> Target {
         // functionality, and a .wasm file.
         exe_suffix: ".js".into(),
         linker: None,
+        pre_link_args,
+        post_link_args,
         relocation_model: RelocModel::Pic,
         panic_strategy: PanicStrategy::Unwind,
         no_default_libraries: false,
-        post_link_args,
         families: cvs!["unix", "wasm"],
-        ..options
+        ..wasm_base::options()
     };
     Target {
         llvm_target: "wasm32-unknown-emscripten".into(),
diff --git a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
index 214b5fce5a6..4e2927dd913 100644
--- a/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/wasm32_unknown_unknown.rs
@@ -29,27 +29,30 @@ pub fn target() -> Target {
     // code on this target due to this ABI mismatch.
     options.default_adjusted_cabi = Some(Abi::Wasm);
 
-    let clang_args = options.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-
-    // Make sure clang uses LLD as its linker and is configured appropriately
-    // otherwise
-    clang_args.push("--target=wasm32-unknown-unknown".into());
-
-    // For now this target just never has an entry symbol no matter the output
-    // type, so unconditionally pass this.
-    clang_args.push("-Wl,--no-entry".into());
-
-    // Rust really needs a way for users to specify exports and imports in
-    // the source code. --export-dynamic isn't the right tool for this job,
-    // however it does have the side effect of automatically exporting a lot
-    // of symbols, which approximates what people want when compiling for
-    // wasm32-unknown-unknown expect, so use it for now.
-    clang_args.push("-Wl,--export-dynamic".into());
-
-    // Add the flags to wasm-ld's args too.
-    let lld_args = options.pre_link_args.entry(LinkerFlavor::Lld(LldFlavor::Wasm)).or_default();
-    lld_args.push("--no-entry".into());
-    lld_args.push("--export-dynamic".into());
+    options.add_pre_link_args(
+        LinkerFlavor::Lld(LldFlavor::Wasm),
+        &[
+            // For now this target just never has an entry symbol no matter the output
+            // type, so unconditionally pass this.
+            "--no-entry",
+            // Rust really needs a way for users to specify exports and imports in
+            // the source code. --export-dynamic isn't the right tool for this job,
+            // however it does have the side effect of automatically exporting a lot
+            // of symbols, which approximates what people want when compiling for
+            // wasm32-unknown-unknown expect, so use it for now.
+            "--export-dynamic",
+        ],
+    );
+    options.add_pre_link_args(
+        LinkerFlavor::Gcc,
+        &[
+            // Make sure clang uses LLD as its linker and is configured appropriately
+            // otherwise
+            "--target=wasm32-unknown-unknown",
+            "-Wl,--no-entry",
+            "-Wl,--export-dynamic",
+        ],
+    );
 
     Target {
         llvm_target: "wasm32-unknown-unknown".into(),
diff --git a/compiler/rustc_target/src/spec/wasm32_wasi.rs b/compiler/rustc_target/src/spec/wasm32_wasi.rs
index 10eb78e4e25..280457d68b9 100644
--- a/compiler/rustc_target/src/spec/wasm32_wasi.rs
+++ b/compiler/rustc_target/src/spec/wasm32_wasi.rs
@@ -80,11 +80,7 @@ pub fn target() -> Target {
 
     options.os = "wasi".into();
     options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
-    options
-        .pre_link_args
-        .entry(LinkerFlavor::Gcc)
-        .or_insert(Vec::new())
-        .push("--target=wasm32-wasi".into());
+    options.add_pre_link_args(LinkerFlavor::Gcc, &["--target=wasm32-wasi"]);
 
     options.pre_link_objects_fallback = crt_objects::pre_wasi_fallback();
     options.post_link_objects_fallback = crt_objects::post_wasi_fallback();
diff --git a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
index 6826c0ac62b..5211f7707fb 100644
--- a/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
+++ b/compiler/rustc_target/src/spec/wasm64_unknown_unknown.rs
@@ -14,19 +14,25 @@ pub fn target() -> Target {
     let mut options = wasm_base::options();
     options.os = "unknown".into();
     options.linker_flavor = LinkerFlavor::Lld(LldFlavor::Wasm);
-    let clang_args = options.pre_link_args.get_mut(&LinkerFlavor::Gcc).unwrap();
 
-    // Make sure clang uses LLD as its linker and is configured appropriately
-    // otherwise
-    clang_args.push("--target=wasm64-unknown-unknown".into());
-
-    // For now this target just never has an entry symbol no matter the output
-    // type, so unconditionally pass this.
-    clang_args.push("-Wl,--no-entry".into());
-
-    let lld_args = options.pre_link_args.get_mut(&LinkerFlavor::Lld(LldFlavor::Wasm)).unwrap();
-    lld_args.push("--no-entry".into());
-    lld_args.push("-mwasm64".into());
+    options.add_pre_link_args(
+        LinkerFlavor::Lld(LldFlavor::Wasm),
+        &[
+            // For now this target just never has an entry symbol no matter the output
+            // type, so unconditionally pass this.
+            "--no-entry",
+            "-mwasm64",
+        ],
+    );
+    options.add_pre_link_args(
+        LinkerFlavor::Gcc,
+        &[
+            // Make sure clang uses LLD as its linker and is configured appropriately
+            // otherwise
+            "--target=wasm64-unknown-unknown",
+            "-Wl,--no-entry",
+        ],
+    );
 
     // Any engine that implements wasm64 will surely implement the rest of these
     // features since they were all merged into the official spec by the time
diff --git a/compiler/rustc_target/src/spec/wasm_base.rs b/compiler/rustc_target/src/spec/wasm_base.rs
index de7b7374af3..5736402ae14 100644
--- a/compiler/rustc_target/src/spec/wasm_base.rs
+++ b/compiler/rustc_target/src/spec/wasm_base.rs
@@ -1,63 +1,56 @@
 use super::crt_objects::CrtObjectsFallback;
 use super::{cvs, LinkerFlavor, LldFlavor, PanicStrategy, RelocModel, TargetOptions, TlsModel};
-use std::collections::BTreeMap;
 
 pub fn options() -> TargetOptions {
-    let mut lld_args = Vec::new();
-    let mut clang_args = Vec::new();
-    let mut arg = |arg: &'static str| {
-        lld_args.push(arg.into());
-        clang_args.push(format!("-Wl,{}", arg).into());
-    };
-
-    // By default LLD only gives us one page of stack (64k) which is a
-    // little small. Default to a larger stack closer to other PC platforms
-    // (1MB) and users can always inject their own link-args to override this.
-    arg("-z");
-    arg("stack-size=1048576");
-
-    // By default LLD's memory layout is:
-    //
-    // 1. First, a blank page
-    // 2. Next, all static data
-    // 3. Finally, the main stack (which grows down)
-    //
-    // This has the unfortunate consequence that on stack overflows you
-    // corrupt static data and can cause some exceedingly weird bugs. To
-    // help detect this a little sooner we instead request that the stack is
-    // placed before static data.
-    //
-    // This means that we'll generate slightly larger binaries as references
-    // to static data will take more bytes in the ULEB128 encoding, but
-    // stack overflow will be guaranteed to trap as it underflows instead of
-    // corrupting static data.
-    arg("--stack-first");
-
-    // FIXME we probably shouldn't pass this but instead pass an explicit list
-    // of symbols we'll allow to be undefined. We don't currently have a
-    // mechanism of knowing, however, which symbols are intended to be imported
-    // from the environment and which are intended to be imported from other
-    // objects linked elsewhere. This is a coarse approximation but is sure to
-    // hide some bugs and frustrate someone at some point, so we should ideally
-    // work towards a world where we can explicitly list symbols that are
-    // supposed to be imported and have all other symbols generate errors if
-    // they remain undefined.
-    arg("--allow-undefined");
-
-    // Rust code should never have warnings, and warnings are often
-    // indicative of bugs, let's prevent them.
-    arg("--fatal-warnings");
-
-    // LLD only implements C++-like demangling, which doesn't match our own
-    // mangling scheme. Tell LLD to not demangle anything and leave it up to
-    // us to demangle these symbols later. Currently rustc does not perform
-    // further demangling, but tools like twiggy and wasm-bindgen are intended
-    // to do so.
-    arg("--no-demangle");
-
-    let mut pre_link_args = BTreeMap::new();
-    pre_link_args.insert(LinkerFlavor::Lld(LldFlavor::Wasm), lld_args);
-    pre_link_args.insert(LinkerFlavor::Gcc, clang_args);
+    macro_rules! args {
+        ($prefix:literal) => {
+            &[
+                // By default LLD only gives us one page of stack (64k) which is a
+                // little small. Default to a larger stack closer to other PC platforms
+                // (1MB) and users can always inject their own link-args to override this.
+                concat!($prefix, "-z"),
+                concat!($prefix, "stack-size=1048576"),
+                // By default LLD's memory layout is:
+                //
+                // 1. First, a blank page
+                // 2. Next, all static data
+                // 3. Finally, the main stack (which grows down)
+                //
+                // This has the unfortunate consequence that on stack overflows you
+                // corrupt static data and can cause some exceedingly weird bugs. To
+                // help detect this a little sooner we instead request that the stack is
+                // placed before static data.
+                //
+                // This means that we'll generate slightly larger binaries as references
+                // to static data will take more bytes in the ULEB128 encoding, but
+                // stack overflow will be guaranteed to trap as it underflows instead of
+                // corrupting static data.
+                concat!($prefix, "--stack-first"),
+                // FIXME we probably shouldn't pass this but instead pass an explicit list
+                // of symbols we'll allow to be undefined. We don't currently have a
+                // mechanism of knowing, however, which symbols are intended to be imported
+                // from the environment and which are intended to be imported from other
+                // objects linked elsewhere. This is a coarse approximation but is sure to
+                // hide some bugs and frustrate someone at some point, so we should ideally
+                // work towards a world where we can explicitly list symbols that are
+                // supposed to be imported and have all other symbols generate errors if
+                // they remain undefined.
+                concat!($prefix, "--allow-undefined"),
+                // Rust code should never have warnings, and warnings are often
+                // indicative of bugs, let's prevent them.
+                concat!($prefix, "--fatal-warnings"),
+                // LLD only implements C++-like demangling, which doesn't match our own
+                // mangling scheme. Tell LLD to not demangle anything and leave it up to
+                // us to demangle these symbols later. Currently rustc does not perform
+                // further demangling, but tools like twiggy and wasm-bindgen are intended
+                // to do so.
+                concat!($prefix, "--no-demangle"),
+            ]
+        };
+    }
+
+    let mut pre_link_args = TargetOptions::link_args(LinkerFlavor::Lld(LldFlavor::Wasm), args!(""));
+    super::add_link_args(&mut pre_link_args, LinkerFlavor::Gcc, args!("-Wl,"));
 
     TargetOptions {
         is_like_wasm: true,
diff --git a/compiler/rustc_target/src/spec/windows_gnu_base.rs b/compiler/rustc_target/src/spec/windows_gnu_base.rs
index d11f1f7d3f8..a0480f386f7 100644
--- a/compiler/rustc_target/src/spec/windows_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnu_base.rs
@@ -1,31 +1,35 @@
 use crate::spec::crt_objects::{self, CrtObjectsFallback};
-use crate::spec::{cvs, LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
+use crate::spec::{cvs, LinkerFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
-    let mut pre_link_args = LinkArgs::new();
-    pre_link_args.insert(
+    let mut pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Ld,
+        &[
+            // Enable ASLR
+            "--dynamicbase",
+            // ASLR will rebase it anyway so leaving that option enabled only leads to confusion
+            "--disable-auto-image-base",
+        ],
+    );
+    super::add_link_args(
+        &mut pre_link_args,
         LinkerFlavor::Gcc,
-        vec![
+        &[
             // Tell GCC to avoid linker plugins, because we are not bundling
             // them with Windows installer, and Rust does its own LTO anyways.
-            "-fno-use-linker-plugin".into(),
-            // Enable ASLR
-            "-Wl,--dynamicbase".into(),
-            // ASLR will rebase it anyway so leaving that option enabled only leads to confusion
-            "-Wl,--disable-auto-image-base".into(),
+            "-fno-use-linker-plugin",
+            "-Wl,--dynamicbase",
+            "-Wl,--disable-auto-image-base",
         ],
     );
 
-    let mut late_link_args = LinkArgs::new();
-    let mut late_link_args_dynamic = LinkArgs::new();
-    let mut late_link_args_static = LinkArgs::new();
     // Order of `late_link_args*` was found through trial and error to work with various
     // mingw-w64 versions (not tested on the CI). It's expected to change from time to time.
-    let mingw_libs = vec![
-        "-lmsvcrt".into(),
-        "-lmingwex".into(),
-        "-lmingw32".into(),
-        "-lgcc".into(), // alas, mingw* libraries above depend on libgcc
+    let mingw_libs = &[
+        "-lmsvcrt",
+        "-lmingwex",
+        "-lmingw32",
+        "-lgcc", // alas, mingw* libraries above depend on libgcc
         // mingw's msvcrt is a weird hybrid import library and static library.
         // And it seems that the linker fails to use import symbols from msvcrt
         // that are required from functions in msvcrt in certain cases. For example
@@ -33,31 +37,27 @@ pub fn opts() -> TargetOptions {
         // The library is purposely listed twice to fix that.
         //
         // See https://github.com/rust-lang/rust/pull/47483 for some more details.
-        "-lmsvcrt".into(),
-        "-luser32".into(),
-        "-lkernel32".into(),
-    ];
-    late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone());
-    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs);
-    let dynamic_unwind_libs = vec![
-        // If any of our crates are dynamically linked then we need to use
-        // the shared libgcc_s-dw2-1.dll. This is required to support
-        // unwinding across DLL boundaries.
-        "-lgcc_s".into(),
-    ];
-    late_link_args_dynamic.insert(LinkerFlavor::Gcc, dynamic_unwind_libs.clone());
-    late_link_args_dynamic.insert(LinkerFlavor::Lld(LldFlavor::Ld), dynamic_unwind_libs);
-    let static_unwind_libs = vec![
-        // If all of our crates are statically linked then we can get away
-        // with statically linking the libgcc unwinding code. This allows
-        // binaries to be redistributed without the libgcc_s-dw2-1.dll
-        // dependency, but unfortunately break unwinding across DLL
-        // boundaries when unwinding across FFI boundaries.
-        "-lgcc_eh".into(),
-        "-l:libpthread.a".into(),
+        "-lmsvcrt",
+        "-luser32",
+        "-lkernel32",
     ];
-    late_link_args_static.insert(LinkerFlavor::Gcc, static_unwind_libs.clone());
-    late_link_args_static.insert(LinkerFlavor::Lld(LldFlavor::Ld), static_unwind_libs);
+    let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs);
+    super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs);
+    // If any of our crates are dynamically linked then we need to use
+    // the shared libgcc_s-dw2-1.dll. This is required to support
+    // unwinding across DLL boundaries.
+    let dynamic_unwind_libs = &["-lgcc_s"];
+    let mut late_link_args_dynamic =
+        TargetOptions::link_args(LinkerFlavor::Ld, dynamic_unwind_libs);
+    super::add_link_args(&mut late_link_args_dynamic, LinkerFlavor::Gcc, dynamic_unwind_libs);
+    // If all of our crates are statically linked then we can get away
+    // with statically linking the libgcc unwinding code. This allows
+    // binaries to be redistributed without the libgcc_s-dw2-1.dll
+    // dependency, but unfortunately break unwinding across DLL
+    // boundaries when unwinding across FFI boundaries.
+    let static_unwind_libs = &["-lgcc_eh", "-l:libpthread.a"];
+    let mut late_link_args_static = TargetOptions::link_args(LinkerFlavor::Ld, static_unwind_libs);
+    super::add_link_args(&mut late_link_args_static, LinkerFlavor::Gcc, static_unwind_libs);
 
     TargetOptions {
         os: "windows".into(),
diff --git a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
index 9f9f8be8718..30f995007a9 100644
--- a/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
+++ b/compiler/rustc_target/src/spec/windows_gnullvm_base.rs
@@ -1,28 +1,17 @@
-use crate::spec::{cvs, LinkArgs, LinkerFlavor, TargetOptions};
+use crate::spec::{cvs, LinkerFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
-    let pre_link_args = LinkArgs::from([(
+    // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
+    // as a path since it's not added to linker search path by the default.
+    // There were attemts to make it behave like libgcc (so one can just use -l<name>)
+    // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
+    let pre_link_args =
+        TargetOptions::link_args(LinkerFlavor::Gcc, &["-nolibc", "--unwindlib=none"]);
+    // Order of `late_link_args*` does not matter with LLD.
+    let late_link_args = TargetOptions::link_args(
         LinkerFlavor::Gcc,
-        vec![
-            // We cannot use `-nodefaultlibs` because compiler-rt has to be passed
-            // as a path since it's not added to linker search path by the default.
-            // There were attemts to make it behave like libgcc (so one can just use -l<name>)
-            // but LLVM maintainers rejected it: https://reviews.llvm.org/D51440
-            "-nolibc".into(),
-            "--unwindlib=none".into(),
-        ],
-    )]);
-    let late_link_args = LinkArgs::from([(
-        LinkerFlavor::Gcc,
-        // Order of `late_link_args*` does not matter with LLD.
-        vec![
-            "-lmingw32".into(),
-            "-lmingwex".into(),
-            "-lmsvcrt".into(),
-            "-lkernel32".into(),
-            "-luser32".into(),
-        ],
-    )]);
+        &["-lmingw32", "-lmingwex", "-lmsvcrt", "-lkernel32", "-luser32"],
+    );
 
     TargetOptions {
         os: "windows".into(),
diff --git a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
index 11968391776..334dec43ef7 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_gnu_base.rs
@@ -1,28 +1,25 @@
-use crate::spec::{LinkArgs, LinkerFlavor, LldFlavor, TargetOptions};
+use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     let base = super::windows_gnu_base::opts();
 
     // FIXME: This should be updated for the exception machinery changes from #67502
     // and inherit from `windows_gnu_base`, at least partially.
-    let mut late_link_args = LinkArgs::new();
+    let mingw_libs = &[
+        "-lwinstorecompat",
+        "-lruntimeobject",
+        "-lsynchronization",
+        "-lvcruntime140_app",
+        "-lucrt",
+        "-lwindowsapp",
+        "-lmingwex",
+        "-lmingw32",
+    ];
+    let mut late_link_args = TargetOptions::link_args(LinkerFlavor::Ld, mingw_libs);
+    super::add_link_args(&mut late_link_args, LinkerFlavor::Gcc, mingw_libs);
+    // Reset the flags back to empty until the FIXME above is addressed.
     let late_link_args_dynamic = LinkArgs::new();
     let late_link_args_static = LinkArgs::new();
-    let mingw_libs = vec![
-        //"-lwinstorecompat".into(),
-        //"-lmingwex".into(),
-        //"-lwinstorecompat".into(),
-        "-lwinstorecompat".into(),
-        "-lruntimeobject".into(),
-        "-lsynchronization".into(),
-        "-lvcruntime140_app".into(),
-        "-lucrt".into(),
-        "-lwindowsapp".into(),
-        "-lmingwex".into(),
-        "-lmingw32".into(),
-    ];
-    late_link_args.insert(LinkerFlavor::Gcc, mingw_libs.clone());
-    late_link_args.insert(LinkerFlavor::Lld(LldFlavor::Ld), mingw_libs);
 
     TargetOptions {
         abi: "uwp".into(),
diff --git a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs
index d6b065b529a..f2573fc2d21 100644
--- a/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs
+++ b/compiler/rustc_target/src/spec/windows_uwp_msvc_base.rs
@@ -1,16 +1,11 @@
-use crate::spec::{LinkerFlavor, LldFlavor, TargetOptions};
+use crate::spec::{LinkerFlavor, TargetOptions};
 
 pub fn opts() -> TargetOptions {
     let mut opts = super::windows_msvc_base::opts();
 
     opts.abi = "uwp".into();
     opts.vendor = "uwp".into();
-    let pre_link_args_msvc = vec!["/APPCONTAINER".into(), "mincore.lib".into()];
-    opts.pre_link_args.entry(LinkerFlavor::Msvc).or_default().extend(pre_link_args_msvc.clone());
-    opts.pre_link_args
-        .entry(LinkerFlavor::Lld(LldFlavor::Link))
-        .or_default()
-        .extend(pre_link_args_msvc);
+    opts.add_pre_link_args(LinkerFlavor::Msvc, &["/APPCONTAINER", "mincore.lib"]);
 
     opts
 }
diff --git a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
index 51d14f0403a..dbd26899c18 100644
--- a/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
+++ b/compiler/rustc_target/src/spec/x86_64_apple_darwin.rs
@@ -6,8 +6,7 @@ pub fn target() -> Target {
     base.cpu = "core2".into();
     base.max_atomic_width = Some(128); // core2 support cmpxchg16b
     base.frame_pointer = FramePointer::Always;
-    base.pre_link_args
-        .insert(LinkerFlavor::Gcc, vec!["-m64".into(), "-arch".into(), "x86_64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-arch", "x86_64"]);
     base.link_env_remove.to_mut().extend(super::apple_base::macos_link_env_remove());
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
diff --git a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
index 47c70513faf..4348d924579 100644
--- a/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
+++ b/compiler/rustc_target/src/spec/x86_64_fortanix_unknown_sgx.rs
@@ -1,41 +1,44 @@
-use std::{borrow::Cow, iter};
+use std::borrow::Cow;
 
 use crate::spec::cvs;
 
 use super::{LinkerFlavor, LldFlavor, Target, TargetOptions};
 
 pub fn target() -> Target {
-    const PRE_LINK_ARGS: &[&str] = &[
-        "-e",
-        "elf_entry",
-        "-Bstatic",
-        "--gc-sections",
-        "-z",
-        "text",
-        "-z",
-        "norelro",
-        "--no-undefined",
-        "--error-unresolved-symbols",
-        "--no-undefined-version",
-        "-Bsymbolic",
-        "--export-dynamic",
-        // The following symbols are needed by libunwind, which is linked after
-        // libstd. Make sure they're included in the link.
-        "-u",
-        "__rust_abort",
-        "-u",
-        "__rust_c_alloc",
-        "-u",
-        "__rust_c_dealloc",
-        "-u",
-        "__rust_print_err",
-        "-u",
-        "__rust_rwlock_rdlock",
-        "-u",
-        "__rust_rwlock_unlock",
-        "-u",
-        "__rust_rwlock_wrlock",
-    ];
+    let pre_link_args = TargetOptions::link_args(
+        LinkerFlavor::Ld,
+        &[
+            "-e",
+            "elf_entry",
+            "-Bstatic",
+            "--gc-sections",
+            "-z",
+            "text",
+            "-z",
+            "norelro",
+            "--no-undefined",
+            "--error-unresolved-symbols",
+            "--no-undefined-version",
+            "-Bsymbolic",
+            "--export-dynamic",
+            // The following symbols are needed by libunwind, which is linked after
+            // libstd. Make sure they're included in the link.
+            "-u",
+            "__rust_abort",
+            "-u",
+            "__rust_c_alloc",
+            "-u",
+            "__rust_c_dealloc",
+            "-u",
+            "__rust_print_err",
+            "-u",
+            "__rust_rwlock_rdlock",
+            "-u",
+            "__rust_rwlock_unlock",
+            "-u",
+            "__rust_rwlock_wrlock",
+        ],
+    );
 
     const EXPORT_SYMBOLS: &[&str] = &[
         "sgx_entry",
@@ -66,11 +69,7 @@ pub fn target() -> Target {
         features: "+rdrnd,+rdseed,+lvi-cfi,+lvi-load-hardening".into(),
         llvm_args: cvs!["--x86-experimental-lvi-inline-asm-hardening"],
         position_independent_executables: true,
-        pre_link_args: iter::once((
-            LinkerFlavor::Lld(LldFlavor::Ld),
-            PRE_LINK_ARGS.iter().cloned().map(Cow::from).collect(),
-        ))
-        .collect(),
+        pre_link_args,
         override_export_symbols: Some(EXPORT_SYMBOLS.iter().cloned().map(Cow::from).collect()),
         relax_elf_relocations: true,
         ..Default::default()
diff --git a/compiler/rustc_target/src/spec/x86_64_linux_android.rs b/compiler/rustc_target/src/spec/x86_64_linux_android.rs
index 049cab0d554..6d19cf26574 100644
--- a/compiler/rustc_target/src/spec/x86_64_linux_android.rs
+++ b/compiler/rustc_target/src/spec/x86_64_linux_android.rs
@@ -6,7 +6,7 @@ pub fn target() -> Target {
     // https://developer.android.com/ndk/guides/abis.html#86-64
     base.features = "+mmx,+sse,+sse2,+sse3,+ssse3,+sse4.1,+sse4.2,+popcnt".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
index 2a697daeb28..0550b221fd9 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_solaris.rs
@@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, StackProbeType, Target};
 
 pub fn target() -> Target {
     let mut base = super::solaris_base::opts();
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.cpu = "x86-64".into();
     base.vendor = "pc".into();
     base.max_atomic_width = Some(64);
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs
index 0fa43481c9b..59a8cffca48 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnu.rs
@@ -1,14 +1,11 @@
-use crate::spec::{LinkerFlavor, LldFlavor, Target};
+use crate::spec::{LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = super::windows_gnu_base::opts();
     base.cpu = "x86-64".into();
-    let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-    gcc_pre_link_args.push("-m64".into());
     // Use high-entropy 64 bit address space for ASLR
-    gcc_pre_link_args.push("-Wl,--high-entropy-va".into());
-    base.pre_link_args
-        .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pep".into()]);
+    base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]);
     base.max_atomic_width = Some(64);
     base.linker = Some("x86_64-w64-mingw32-gcc".into());
 
diff --git a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
index b5ff63e0532..d3909b3895e 100644
--- a/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
+++ b/compiler/rustc_target/src/spec/x86_64_pc_windows_gnullvm.rs
@@ -3,8 +3,7 @@ use crate::spec::{LinkerFlavor, Target};
 pub fn target() -> Target {
     let mut base = super::windows_gnullvm_base::opts();
     base.cpu = "x86-64".into();
-    let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-    gcc_pre_link_args.push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.max_atomic_width = Some(64);
     base.linker = Some("x86_64-w64-mingw32-clang".into());
 
diff --git a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
index a02018266fb..cbe87589a70 100644
--- a/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
+++ b/compiler/rustc_target/src/spec/x86_64_sun_solaris.rs
@@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, StackProbeType, Target};
 
 pub fn target() -> Target {
     let mut base = super::solaris_base::opts();
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     base.cpu = "x86-64".into();
     base.vendor = "sun".into();
     base.max_atomic_width = Some(64);
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
index 1f2b998a7ba..746f6478178 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_dragonfly.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::dragonfly_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
index c9aedd6ea82..b30784ed692 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_freebsd.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::freebsd_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.supported_sanitizers =
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
index aebbd18c66a..d6d03362982 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_haiku.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::haiku_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     // This option is required to build executables on Haiku x86_64
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
index 9529fa9640d..9f19c3a2b2a 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_illumos.rs
@@ -2,7 +2,7 @@ use crate::spec::{LinkerFlavor, SanitizerSet, Target};
 
 pub fn target() -> Target {
     let mut base = super::illumos_base::opts();
-    base.pre_link_args.insert(LinkerFlavor::Gcc, vec!["-m64".into(), "-std=c99".into()]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-std=c99"]);
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
     base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
index e525cfdde14..956be0353fa 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnu.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::linux_gnu_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.static_position_independent_executables = true;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs
index 863b41633e2..140882747c2 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_gnux32.rs
@@ -5,7 +5,7 @@ pub fn target() -> Target {
     base.cpu = "x86-64".into();
     base.abi = "x32".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-mx32".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-mx32"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.has_thread_local = false;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
index 8678f06e2cb..87e7784d1f9 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_linux_musl.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::linux_musl_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.static_position_independent_executables = true;
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
index a7115dace1c..d3a67619aa8 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_netbsd.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::netbsd_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.supported_sanitizers = SanitizerSet::ADDRESS
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs
index 0db88d64ac0..593345a5f1d 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_none_linuxkernel.rs
@@ -10,7 +10,7 @@ pub fn target() -> Target {
     base.features =
         "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float".into();
     base.code_model = Some(CodeModel::Kernel);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
 
     Target {
         // FIXME: Some dispute, the linux-on-clang folks think this should use
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
index 11e9cc4abc0..f50c6bceec9 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_openbsd.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::openbsd_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
index af8b9673c30..668ae905417 100644
--- a/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
+++ b/compiler/rustc_target/src/spec/x86_64_unknown_redox.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::redox_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
 
diff --git a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs
index a94bbbf6ede..76d2013cf7f 100644
--- a/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs
+++ b/compiler/rustc_target/src/spec/x86_64_uwp_windows_gnu.rs
@@ -1,14 +1,11 @@
-use crate::spec::{LinkerFlavor, LldFlavor, Target};
+use crate::spec::{LinkerFlavor, Target};
 
 pub fn target() -> Target {
     let mut base = super::windows_uwp_gnu_base::opts();
     base.cpu = "x86-64".into();
-    let gcc_pre_link_args = base.pre_link_args.entry(LinkerFlavor::Gcc).or_default();
-    gcc_pre_link_args.push("-m64".into());
     // Use high-entropy 64 bit address space for ASLR
-    gcc_pre_link_args.push("-Wl,--high-entropy-va".into());
-    base.pre_link_args
-        .insert(LinkerFlavor::Lld(LldFlavor::Ld), vec!["-m".into(), "i386pep".into()]);
+    base.add_pre_link_args(LinkerFlavor::Ld, &["-m", "i386pep", "--high-entropy-va"]);
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64", "-Wl,--high-entropy-va"]);
     base.max_atomic_width = Some(64);
 
     Target {
diff --git a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
index 16d29753e7d..1298974952f 100644
--- a/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
+++ b/compiler/rustc_target/src/spec/x86_64_wrs_vxworks.rs
@@ -4,7 +4,7 @@ pub fn target() -> Target {
     let mut base = super::vxworks_base::opts();
     base.cpu = "x86-64".into();
     base.max_atomic_width = Some(64);
-    base.pre_link_args.entry(LinkerFlavor::Gcc).or_default().push("-m64".into());
+    base.add_pre_link_args(LinkerFlavor::Gcc, &["-m64"]);
     // don't use probe-stack=inline-asm until rust#83139 and rust#84667 are resolved
     base.stack_probes = StackProbeType::Call;
     base.disable_redzone = true;
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 95f1e224a4c..a1861529b59 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -452,7 +452,7 @@ fn report_conflicting_impls(
     match used_to_be_allowed {
         None => {
             let reported = if overlap.with_impl.is_local()
-                || !tcx.orphan_check_crate(()).contains(&impl_def_id)
+                || tcx.orphan_check_impl(impl_def_id).is_ok()
             {
                 let err = struct_span_err!(tcx.sess, impl_span, E0119, "");
                 Some(decorate(
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs
index 79312153895..e78d58dc210 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_typeck/src/check/wfcheck.rs
@@ -990,6 +990,15 @@ fn check_type_defn<'tcx, F>(
         let packed = tcx.adt_def(item.def_id).repr().packed();
 
         for variant in &variants {
+            // All field types must be well-formed.
+            for field in &variant.fields {
+                fcx.register_wf_obligation(
+                    field.ty.into(),
+                    field.span,
+                    ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))),
+                )
+            }
+
             // For DST, or when drop needs to copy things around, all
             // intermediate types must be sized.
             let needs_drop_copy = || {
@@ -1006,6 +1015,7 @@ fn check_type_defn<'tcx, F>(
                     }
                 }
             };
+            // All fields (except for possibly the last) should be sized.
             let all_sized = all_sized || variant.fields.is_empty() || needs_drop_copy();
             let unsized_len = if all_sized { 0 } else { 1 };
             for (idx, field) in
@@ -1030,15 +1040,6 @@ fn check_type_defn<'tcx, F>(
                 );
             }
 
-            // All field types must be well-formed.
-            for field in &variant.fields {
-                fcx.register_wf_obligation(
-                    field.ty.into(),
-                    field.span,
-                    ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(field.def_id))),
-                )
-            }
-
             // Explicit `enum` discriminant values must const-evaluate successfully.
             if let Some(discr_def_id) = variant.explicit_discr {
                 let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id());
diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs
index 3903448a007..447ec87f302 100644
--- a/compiler/rustc_typeck/src/coherence/mod.rs
+++ b/compiler/rustc_typeck/src/coherence/mod.rs
@@ -146,7 +146,7 @@ pub fn provide(providers: &mut Providers) {
     use self::builtin::coerce_unsized_info;
     use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls};
     use self::inherent_impls_overlap::crate_inherent_impls_overlap_check;
-    use self::orphan::orphan_check_crate;
+    use self::orphan::orphan_check_impl;
 
     *providers = Providers {
         coherent_trait,
@@ -155,7 +155,7 @@ pub fn provide(providers: &mut Providers) {
         inherent_impls,
         crate_inherent_impls_overlap_check,
         coerce_unsized_info,
-        orphan_check_crate,
+        orphan_check_impl,
         ..*providers
     };
 }
@@ -171,21 +171,12 @@ fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) {
 
         check_impl(tcx, impl_def_id, trait_ref);
         check_object_overlap(tcx, impl_def_id, trait_ref);
-    }
-    builtin::check_trait(tcx, def_id);
-}
 
-pub fn check_coherence(tcx: TyCtxt<'_>) {
-    tcx.sess.time("unsafety_checking", || unsafety::check(tcx));
-    tcx.ensure().orphan_check_crate(());
-
-    for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
-        tcx.ensure().coherent_trait(trait_def_id);
+        tcx.sess.time("unsafety_checking", || unsafety::check_item(tcx, impl_def_id));
+        tcx.sess.time("orphan_checking", || tcx.ensure().orphan_check_impl(impl_def_id));
     }
 
-    // these queries are executed for side-effects (error reporting):
-    tcx.ensure().crate_inherent_impls(());
-    tcx.ensure().crate_inherent_impls_overlap_check(());
+    builtin::check_trait(tcx, def_id);
 }
 
 /// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`.
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs
index 9ddfc8d5cc8..f3a043a08a3 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_typeck/src/coherence/orphan.rs
@@ -18,26 +18,29 @@ use rustc_span::Span;
 use rustc_trait_selection::traits;
 use std::ops::ControlFlow;
 
-pub(super) fn orphan_check_crate(tcx: TyCtxt<'_>, (): ()) -> &[LocalDefId] {
-    let mut errors = Vec::new();
-    for (&trait_def_id, impls_of_trait) in tcx.all_local_trait_impls(()) {
-        for &impl_of_trait in impls_of_trait {
-            match orphan_check_impl(tcx, impl_of_trait) {
-                Ok(()) => {}
-                Err(_) => errors.push(impl_of_trait),
-            }
-        }
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn orphan_check_impl(
+    tcx: TyCtxt<'_>,
+    impl_def_id: LocalDefId,
+) -> Result<(), ErrorGuaranteed> {
+    let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
+    if let Some(err) = trait_ref.error_reported() {
+        return Err(err);
+    }
 
-        if tcx.trait_is_auto(trait_def_id) {
-            lint_auto_trait_impls(tcx, trait_def_id, impls_of_trait);
-        }
+    let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id);
+    if tcx.trait_is_auto(trait_ref.def_id) {
+        lint_auto_trait_impl(tcx, trait_ref, impl_def_id);
     }
-    tcx.arena.alloc_slice(&errors)
+
+    ret
 }
 
-#[instrument(skip(tcx), level = "debug")]
-fn orphan_check_impl(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
-    let trait_ref = tcx.impl_trait_ref(def_id).unwrap();
+fn do_orphan_check_impl<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+    def_id: LocalDefId,
+) -> Result<(), ErrorGuaranteed> {
     let trait_def_id = trait_ref.def_id;
 
     let item = tcx.hir().item(hir::ItemId { def_id });
@@ -329,89 +332,82 @@ fn emit_orphan_check_error<'tcx>(
 
 /// Lint impls of auto traits if they are likely to have
 /// unsound or surprising effects on auto impls.
-fn lint_auto_trait_impls(tcx: TyCtxt<'_>, trait_def_id: DefId, impls: &[LocalDefId]) {
-    let mut non_covering_impls = Vec::new();
-    for &impl_def_id in impls {
-        let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap();
-        if trait_ref.references_error() {
-            return;
-        }
+fn lint_auto_trait_impl<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_ref: ty::TraitRef<'tcx>,
+    impl_def_id: LocalDefId,
+) {
+    if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive {
+        return;
+    }
 
-        if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive {
+    assert_eq!(trait_ref.substs.len(), 1);
+    let self_ty = trait_ref.self_ty();
+    let (self_type_did, substs) = match self_ty.kind() {
+        ty::Adt(def, substs) => (def.did(), substs),
+        _ => {
+            // FIXME: should also lint for stuff like `&i32` but
+            // considering that auto traits are unstable, that
+            // isn't too important for now as this only affects
+            // crates using `nightly`, and std.
             return;
         }
+    };
 
-        assert_eq!(trait_ref.substs.len(), 1);
-        let self_ty = trait_ref.self_ty();
-        let (self_type_did, substs) = match self_ty.kind() {
-            ty::Adt(def, substs) => (def.did(), substs),
-            _ => {
-                // FIXME: should also lint for stuff like `&i32` but
-                // considering that auto traits are unstable, that
-                // isn't too important for now as this only affects
-                // crates using `nightly`, and std.
-                continue;
-            }
-        };
+    // Impls which completely cover a given root type are fine as they
+    // disable auto impls entirely. So only lint if the substs
+    // are not a permutation of the identity substs.
+    let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else {
+        // ok
+        return;
+    };
 
-        // Impls which completely cover a given root type are fine as they
-        // disable auto impls entirely. So only lint if the substs
-        // are not a permutation of the identity substs.
-        match tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) {
-            Ok(()) => {} // ok
-            Err(arg) => {
-                // Ideally:
-                //
-                // - compute the requirements for the auto impl candidate
-                // - check whether these are implied by the non covering impls
-                // - if not, emit the lint
-                //
-                // What we do here is a bit simpler:
-                //
-                // - badly check if an auto impl candidate definitely does not apply
-                //   for the given simplified type
-                // - if so, do not lint
-                if fast_reject_auto_impl(tcx, trait_def_id, self_ty) {
-                    // ok
-                } else {
-                    non_covering_impls.push((impl_def_id, self_type_did, arg));
-                }
-            }
-        }
+    // Ideally:
+    //
+    // - compute the requirements for the auto impl candidate
+    // - check whether these are implied by the non covering impls
+    // - if not, emit the lint
+    //
+    // What we do here is a bit simpler:
+    //
+    // - badly check if an auto impl candidate definitely does not apply
+    //   for the given simplified type
+    // - if so, do not lint
+    if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) {
+        // ok
+        return;
     }
 
-    for &(impl_def_id, self_type_did, arg) in &non_covering_impls {
-        tcx.struct_span_lint_hir(
-            lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
-            tcx.hir().local_def_id_to_hir_id(impl_def_id),
-            tcx.def_span(impl_def_id),
-            |err| {
-                let item_span = tcx.def_span(self_type_did);
-                let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
-                let mut err = err.build(&format!(
-                    "cross-crate traits with a default impl, like `{}`, \
+    tcx.struct_span_lint_hir(
+        lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
+        tcx.hir().local_def_id_to_hir_id(impl_def_id),
+        tcx.def_span(impl_def_id),
+        |err| {
+            let item_span = tcx.def_span(self_type_did);
+            let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
+            let mut err = err.build(&format!(
+                "cross-crate traits with a default impl, like `{}`, \
                          should not be specialized",
-                    tcx.def_path_str(trait_def_id),
-                ));
-                match arg {
-                    ty::util::NotUniqueParam::DuplicateParam(arg) => {
-                        err.note(&format!("`{}` is mentioned multiple times", arg));
-                    }
-                    ty::util::NotUniqueParam::NotParam(arg) => {
-                        err.note(&format!("`{}` is not a generic parameter", arg));
-                    }
+                tcx.def_path_str(trait_ref.def_id),
+            ));
+            match arg {
+                ty::util::NotUniqueParam::DuplicateParam(arg) => {
+                    err.note(&format!("`{}` is mentioned multiple times", arg));
                 }
-                err.span_note(
-                    item_span,
-                    &format!(
-                        "try using the same sequence of generic parameters as the {} definition",
-                        self_descr,
-                    ),
-                );
-                err.emit();
-            },
-        );
-    }
+                ty::util::NotUniqueParam::NotParam(arg) => {
+                    err.note(&format!("`{}` is not a generic parameter", arg));
+                }
+            }
+            err.span_note(
+                item_span,
+                &format!(
+                    "try using the same sequence of generic parameters as the {} definition",
+                    self_descr,
+                ),
+            );
+            err.emit();
+        },
+    );
 }
 
 fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool {
diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs
index 3cfc96ccbfd..e45fb5fe41c 100644
--- a/compiler/rustc_typeck/src/coherence/unsafety.rs
+++ b/compiler/rustc_typeck/src/coherence/unsafety.rs
@@ -6,37 +6,18 @@ use rustc_hir as hir;
 use rustc_hir::def::DefKind;
 use rustc_hir::Unsafety;
 use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::LocalDefId;
 
-pub fn check(tcx: TyCtxt<'_>) {
-    for id in tcx.hir().items() {
-        if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
-            let item = tcx.hir().item(id);
-            if let hir::ItemKind::Impl(ref impl_) = item.kind {
-                check_unsafety_coherence(
-                    tcx,
-                    item,
-                    Some(&impl_.generics),
-                    impl_.unsafety,
-                    impl_.polarity,
-                );
-            }
-        }
-    }
-}
+pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+    debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
+    let item = tcx.hir().expect_item(def_id);
+    let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
 
-fn check_unsafety_coherence<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    item: &hir::Item<'_>,
-    impl_generics: Option<&hir::Generics<'_>>,
-    unsafety: hir::Unsafety,
-    polarity: hir::ImplPolarity,
-) {
     if let Some(trait_ref) = tcx.impl_trait_ref(item.def_id) {
         let trait_def = tcx.trait_def(trait_ref.def_id);
-        let unsafe_attr = impl_generics.and_then(|generics| {
-            generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle")
-        });
-        match (trait_def.unsafety, unsafe_attr, unsafety, polarity) {
+        let unsafe_attr =
+            impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
+        match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
             (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
                 struct_span_err!(
                     tcx.sess,
diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs
index d613edf0ab0..a7a008bc2eb 100644
--- a/compiler/rustc_typeck/src/lib.rs
+++ b/compiler/rustc_typeck/src/lib.rs
@@ -513,7 +513,15 @@ pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
     })?;
 
     tcx.sess.track_errors(|| {
-        tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx));
+        tcx.sess.time("coherence_checking", || {
+            for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
+                tcx.ensure().coherent_trait(trait_def_id);
+            }
+
+            // these queries are executed for side-effects (error reporting):
+            tcx.ensure().crate_inherent_impls(());
+            tcx.ensure().crate_inherent_impls_overlap_check(());
+        });
     })?;
 
     if tcx.features().rustc_attrs {
diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs
index ea410849694..9cd8b6d1455 100644
--- a/src/bootstrap/native.rs
+++ b/src/bootstrap/native.rs
@@ -160,7 +160,7 @@ pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
         // files in the tarball are in the past, so it doesn't trigger a
         // rebuild.
         let now = filetime::FileTime::from_system_time(std::time::SystemTime::now());
-        let llvm_config = llvm_root.join("bin/llvm-config");
+        let llvm_config = llvm_root.join("bin").join(exe("llvm-config", builder.config.build));
         t!(filetime::set_file_times(&llvm_config, now, now));
 
         let llvm_lib = llvm_root.join("lib");
diff --git a/src/test/ui/chalkify/bugs/async.rs b/src/test/ui/chalkify/bugs/async.rs
index 58fc93064ed..ae5224dbd6f 100644
--- a/src/test/ui/chalkify/bugs/async.rs
+++ b/src/test/ui/chalkify/bugs/async.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: unknown
 // compile-flags: -Z chalk --edition=2021
 
 fn main() -> () {}
diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr
index 91289148079..c364c707ff9 100644
--- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr
+++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-negative.stderr
@@ -1,3 +1,15 @@
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
+  --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
+   |
+LL | impl !Marker1 for dyn Object + Marker2 { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
+  --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
+   |
+LL | impl !Marker2 for dyn Object + Marker2 { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
   --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1
    |
@@ -21,18 +33,6 @@ error[E0321]: cross-crate traits with a default impl, like `Send`, can only be i
 LL | impl !Send for dyn Object + Marker2 {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
 
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
-  --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
-   |
-LL | impl !Marker1 for dyn Object + Marker2 { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
-
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
-  --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
-   |
-LL | impl !Marker2 for dyn Object + Marker2 { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
-
 error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0117, E0321, E0371.
diff --git a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr
index 056198374a4..b80429794f9 100644
--- a/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr
+++ b/src/test/ui/coherence/coherence-impl-trait-for-marker-trait-positive.stderr
@@ -1,3 +1,15 @@
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
+  --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
+   |
+LL | impl Marker1 for dyn Object + Marker2 { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+
+error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
+  --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1
+   |
+LL | impl Marker2 for dyn Object + Marker2 { }
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
   --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:23:1
    |
@@ -21,18 +33,6 @@ error[E0321]: cross-crate traits with a default impl, like `Send`, can only be i
 LL | unsafe impl Send for dyn Object + Marker2 {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
 
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
-  --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
-   |
-LL | impl Marker1 for dyn Object + Marker2 { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
-
-error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
-  --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1
-   |
-LL | impl Marker2 for dyn Object + Marker2 { }
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
-
 error: aborting due to 5 previous errors
 
 Some errors have detailed explanations: E0117, E0321, E0371.
diff --git a/src/test/ui/coherence/coherence-impls-copy.stderr b/src/test/ui/coherence/coherence-impls-copy.stderr
index b3ca354c633..86356af2564 100644
--- a/src/test/ui/coherence/coherence-impls-copy.stderr
+++ b/src/test/ui/coherence/coherence-impls-copy.stderr
@@ -9,49 +9,49 @@ LL | impl Copy for i32 {}
    |
    = note: define and implement a trait or new type instead
 
+error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`
+  --> $DIR/coherence-impls-copy.rs:28:1
+   |
+LL | impl Copy for &'static NotSync {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: conflicting implementation in crate `core`:
+           - impl<T> Copy for &T
+             where T: ?Sized;
+
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-copy.rs:25:1
+  --> $DIR/coherence-impls-copy.rs:33:1
    |
-LL | impl Copy for (MyType, MyType) {}
-   | ^^^^^^^^^^^^^^----------------
+LL | impl Copy for &'static [NotSync] {}
+   | ^^^^^^^^^^^^^^------------------
    | |             |
-   | |             this is not defined in the current crate because tuples are always foreign
+   | |             this is not defined in the current crate because slices are always foreign
    | impl doesn't use only types from inside the current crate
    |
    = note: define and implement a trait or new type instead
 
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-copy.rs:30:1
+  --> $DIR/coherence-impls-copy.rs:25:1
    |
-LL | impl Copy for [MyType] {}
-   | ^^^^^^^^^^^^^^--------
+LL | impl Copy for (MyType, MyType) {}
+   | ^^^^^^^^^^^^^^----------------
    | |             |
-   | |             this is not defined in the current crate because slices are always foreign
+   | |             this is not defined in the current crate because tuples are always foreign
    | impl doesn't use only types from inside the current crate
    |
    = note: define and implement a trait or new type instead
 
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-copy.rs:33:1
+  --> $DIR/coherence-impls-copy.rs:30:1
    |
-LL | impl Copy for &'static [NotSync] {}
-   | ^^^^^^^^^^^^^^------------------
+LL | impl Copy for [MyType] {}
+   | ^^^^^^^^^^^^^^--------
    | |             |
    | |             this is not defined in the current crate because slices are always foreign
    | impl doesn't use only types from inside the current crate
    |
    = note: define and implement a trait or new type instead
 
-error[E0119]: conflicting implementations of trait `std::marker::Copy` for type `&NotSync`
-  --> $DIR/coherence-impls-copy.rs:28:1
-   |
-LL | impl Copy for &'static NotSync {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: conflicting implementation in crate `core`:
-           - impl<T> Copy for &T
-             where T: ?Sized;
-
 error[E0206]: the trait `Copy` may not be implemented for this type
   --> $DIR/coherence-impls-copy.rs:21:15
    |
diff --git a/src/test/ui/coherence/coherence-impls-send.stderr b/src/test/ui/coherence/coherence-impls-send.stderr
index dd1fd1b0dce..e1071846e14 100644
--- a/src/test/ui/coherence/coherence-impls-send.stderr
+++ b/src/test/ui/coherence/coherence-impls-send.stderr
@@ -1,4 +1,15 @@
 error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/coherence-impls-send.rs:25:1
+   |
+LL | unsafe impl Send for &'static [NotSync] {}
+   | ^^^^^^^^^^^^^^^^^^^^^------------------
+   | |                    |
+   | |                    this is not defined in the current crate because slices are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
   --> $DIR/coherence-impls-send.rs:16:1
    |
 LL | unsafe impl Send for (MyType, MyType) {}
@@ -26,17 +37,6 @@ LL | unsafe impl Send for [MyType] {}
    |
    = note: define and implement a trait or new type instead
 
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-send.rs:25:1
-   |
-LL | unsafe impl Send for &'static [NotSync] {}
-   | ^^^^^^^^^^^^^^^^^^^^^------------------
-   | |                    |
-   | |                    this is not defined in the current crate because slices are always foreign
-   | impl doesn't use only types from inside the current crate
-   |
-   = note: define and implement a trait or new type instead
-
 error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0117, E0321.
diff --git a/src/test/ui/coherence/coherence-impls-sized.stderr b/src/test/ui/coherence/coherence-impls-sized.stderr
index e1e4acd4cd8..17a7544521d 100644
--- a/src/test/ui/coherence/coherence-impls-sized.stderr
+++ b/src/test/ui/coherence/coherence-impls-sized.stderr
@@ -1,36 +1,3 @@
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-sized.rs:20:1
-   |
-LL | impl Sized for (MyType, MyType) {}
-   | ^^^^^^^^^^^^^^^----------------
-   | |              |
-   | |              this is not defined in the current crate because tuples are always foreign
-   | impl doesn't use only types from inside the current crate
-   |
-   = note: define and implement a trait or new type instead
-
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-sized.rs:27:1
-   |
-LL | impl Sized for [MyType] {}
-   | ^^^^^^^^^^^^^^^--------
-   | |              |
-   | |              this is not defined in the current crate because slices are always foreign
-   | impl doesn't use only types from inside the current crate
-   |
-   = note: define and implement a trait or new type instead
-
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
-  --> $DIR/coherence-impls-sized.rs:31:1
-   |
-LL | impl Sized for &'static [NotSync] {}
-   | ^^^^^^^^^^^^^^^------------------
-   | |              |
-   | |              this is not defined in the current crate because slices are always foreign
-   | impl doesn't use only types from inside the current crate
-   |
-   = note: define and implement a trait or new type instead
-
 error[E0322]: explicit impls for the `Sized` trait are not permitted
   --> $DIR/coherence-impls-sized.rs:14:1
    |
@@ -49,6 +16,17 @@ error[E0322]: explicit impls for the `Sized` trait are not permitted
 LL | impl Sized for (MyType, MyType) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Sized` not allowed
 
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/coherence-impls-sized.rs:20:1
+   |
+LL | impl Sized for (MyType, MyType) {}
+   | ^^^^^^^^^^^^^^^----------------
+   | |              |
+   | |              this is not defined in the current crate because tuples are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
 error[E0322]: explicit impls for the `Sized` trait are not permitted
   --> $DIR/coherence-impls-sized.rs:24:1
    |
@@ -61,12 +39,34 @@ error[E0322]: explicit impls for the `Sized` trait are not permitted
 LL | impl Sized for [MyType] {}
    | ^^^^^^^^^^^^^^^^^^^^^^^ impl of `Sized` not allowed
 
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/coherence-impls-sized.rs:27:1
+   |
+LL | impl Sized for [MyType] {}
+   | ^^^^^^^^^^^^^^^--------
+   | |              |
+   | |              this is not defined in the current crate because slices are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
 error[E0322]: explicit impls for the `Sized` trait are not permitted
   --> $DIR/coherence-impls-sized.rs:31:1
    |
 LL | impl Sized for &'static [NotSync] {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Sized` not allowed
 
+error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+  --> $DIR/coherence-impls-sized.rs:31:1
+   |
+LL | impl Sized for &'static [NotSync] {}
+   | ^^^^^^^^^^^^^^^------------------
+   | |              |
+   | |              this is not defined in the current crate because slices are always foreign
+   | impl doesn't use only types from inside the current crate
+   |
+   = note: define and implement a trait or new type instead
+
 error: aborting due to 9 previous errors
 
 Some errors have detailed explanations: E0117, E0322.
diff --git a/src/test/ui/coherence/coherence-with-closure.stderr b/src/test/ui/coherence/coherence-with-closure.stderr
index 20b986cee69..d2ca63fa146 100644
--- a/src/test/ui/coherence/coherence-with-closure.stderr
+++ b/src/test/ui/coherence/coherence-with-closure.stderr
@@ -1,3 +1,12 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueClosure>`
+  --> $DIR/coherence-with-closure.rs:12:1
+   |
+LL | impl Trait for Wrapper<OpaqueClosure> {}
+   | ------------------------------------- first implementation here
+LL |
+LL | impl<T: Sync> Trait for Wrapper<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueClosure>`
+
 error: cannot implement trait on type alias impl trait
   --> $DIR/coherence-with-closure.rs:10:24
    |
@@ -10,15 +19,6 @@ note: type alias impl trait defined here
 LL | type OpaqueClosure = impl Sized;
    |                      ^^^^^^^^^^
 
-error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueClosure>`
-  --> $DIR/coherence-with-closure.rs:12:1
-   |
-LL | impl Trait for Wrapper<OpaqueClosure> {}
-   | ------------------------------------- first implementation here
-LL |
-LL | impl<T: Sync> Trait for Wrapper<T> {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueClosure>`
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/coherence/coherence-with-generator.stderr b/src/test/ui/coherence/coherence-with-generator.stderr
index 249ad3cb9ec..804bc1c3a6d 100644
--- a/src/test/ui/coherence/coherence-with-generator.stderr
+++ b/src/test/ui/coherence/coherence-with-generator.stderr
@@ -1,3 +1,12 @@
+error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
+  --> $DIR/coherence-with-generator.rs:16:1
+   |
+LL | impl Trait for Wrapper<OpaqueGenerator> {}
+   | --------------------------------------- first implementation here
+LL |
+LL | impl<T: Sync> Trait for Wrapper<T> {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueGenerator>`
+
 error: cannot implement trait on type alias impl trait
   --> $DIR/coherence-with-generator.rs:14:24
    |
@@ -10,15 +19,6 @@ note: type alias impl trait defined here
 LL | type OpaqueGenerator = impl Sized;
    |                        ^^^^^^^^^^
 
-error[E0119]: conflicting implementations of trait `Trait` for type `Wrapper<OpaqueGenerator>`
-  --> $DIR/coherence-with-generator.rs:16:1
-   |
-LL | impl Trait for Wrapper<OpaqueGenerator> {}
-   | --------------------------------------- first implementation here
-LL |
-LL | impl<T: Sync> Trait for Wrapper<T> {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Wrapper<OpaqueGenerator>`
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/generic-associated-types/bugs/issue-80626.rs b/src/test/ui/generic-associated-types/bugs/issue-80626.rs
index a637da6cf6f..14f27aff1cc 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-80626.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-80626.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #80626
 
 // This should pass, but it requires `Sized` to be coinductive.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-80626.stderr b/src/test/ui/generic-associated-types/bugs/issue-80626.stderr
index 8b0cc78e999..487b83dfa3f 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-80626.stderr
+++ b/src/test/ui/generic-associated-types/bugs/issue-80626.stderr
@@ -4,16 +4,11 @@ error[E0275]: overflow evaluating the requirement `LinkedList<A>: Sized`
 LL |     Next(A::Allocated<Self>)
    |          ^^^^^^^^^^^^^^^^^^
    |
-   = note: no field of an enum variant may have a dynamically sized type
-   = help: change the field's type to have a statically known size
-help: borrowed types always have a statically known size
+note: required by a bound in `Allocator::Allocated`
+  --> $DIR/issue-80626.rs:9:20
    |
-LL |     Next(&A::Allocated<Self>)
-   |          +
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     Next(Box<A::Allocated<Self>>)
-   |          ++++                  +
+LL |     type Allocated<T>;
+   |                    ^ required by this bound in `Allocator::Allocated`
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-86218.rs b/src/test/ui/generic-associated-types/bugs/issue-86218.rs
index 68cd0fd7efc..fb62c10a9e3 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-86218.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-86218.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #86218
 
 // This should pass, but seems to run into a TAIT issue.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87735.rs b/src/test/ui/generic-associated-types/bugs/issue-87735.rs
index 53e3ad7fe69..0844d84c34f 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-87735.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-87735.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #87735, #88526
 
 // This should pass, but we need an extension of implied bounds (probably).
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87748.rs b/src/test/ui/generic-associated-types/bugs/issue-87748.rs
index 6e7cd45bdb1..a3d00ee03b1 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-87748.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-87748.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #87748
 
 // This should pass, but unnormalized input args aren't treated as implied.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87755.rs b/src/test/ui/generic-associated-types/bugs/issue-87755.rs
index 31cea12a3e2..efa487d624f 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-87755.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-87755.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #87755
 
 // This should pass.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-87803.rs b/src/test/ui/generic-associated-types/bugs/issue-87803.rs
index 57a4b028d93..a8a111c99ef 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-87803.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-87803.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #87803
 
 // This should pass, but using a type alias vs a reference directly
 // changes late-bound -> early-bound.
diff --git a/src/test/ui/generic-associated-types/bugs/issue-88382.rs b/src/test/ui/generic-associated-types/bugs/issue-88382.rs
index c9f34240527..5493b9b9391 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-88382.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-88382.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #88382
 
 // This should pass, but has a missed normalization due to HRTB.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-88460.rs b/src/test/ui/generic-associated-types/bugs/issue-88460.rs
index b31d012d2fc..f1c3b226915 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-88460.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-88460.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #88460
 
 // This should pass, but has a missed normalization due to HRTB.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-88526.rs b/src/test/ui/generic-associated-types/bugs/issue-88526.rs
index c72a450b926..15363ad04bf 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-88526.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-88526.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #88526
 
 // This should pass, but requires more logic.
 
diff --git a/src/test/ui/generic-associated-types/bugs/issue-89008.rs b/src/test/ui/generic-associated-types/bugs/issue-89008.rs
index 1581b7105a8..79c28b0d221 100644
--- a/src/test/ui/generic-associated-types/bugs/issue-89008.rs
+++ b/src/test/ui/generic-associated-types/bugs/issue-89008.rs
@@ -1,6 +1,6 @@
 // check-fail
 // edition:2021
-// known-bug
+// known-bug: #88908
 
 // This should pass, but seems to run into a TAIT bug.
 
diff --git a/src/test/ui/hrtb/issue-95034.rs b/src/test/ui/hrtb/issue-95034.rs
index aee6fe61ba8..d8edbe7e56b 100644
--- a/src/test/ui/hrtb/issue-95034.rs
+++ b/src/test/ui/hrtb/issue-95034.rs
@@ -1,4 +1,4 @@
-// known-bug
+// known-bug: #95034
 // failure-status: 101
 // compile-flags: --edition=2021 --crate-type=lib
 // rustc-env:RUST_BACKTRACE=0
diff --git a/src/test/ui/impl-trait/auto-trait.stderr b/src/test/ui/impl-trait/auto-trait.stderr
index 3b360f492b7..5e10272b0db 100644
--- a/src/test/ui/impl-trait/auto-trait.stderr
+++ b/src/test/ui/impl-trait/auto-trait.stderr
@@ -1,3 +1,12 @@
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
+  --> $DIR/auto-trait.rs:21:1
+   |
+LL | impl<T: Send> AnotherTrait for T {}
+   | -------------------------------- first implementation here
+...
+LL | impl AnotherTrait for D<OpaqueType> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
+
 error: cannot implement trait on type alias impl trait
   --> $DIR/auto-trait.rs:21:25
    |
@@ -10,15 +19,6 @@ note: type alias impl trait defined here
 LL | type OpaqueType = impl OpaqueTrait;
    |                   ^^^^^^^^^^^^^^^^
 
-error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
-  --> $DIR/auto-trait.rs:21:1
-   |
-LL | impl<T: Send> AnotherTrait for T {}
-   | -------------------------------- first implementation here
-...
-LL | impl AnotherTrait for D<OpaqueType> {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/impl-trait/negative-reasoning.stderr b/src/test/ui/impl-trait/negative-reasoning.stderr
index 98f9fbd8fef..479b451855d 100644
--- a/src/test/ui/impl-trait/negative-reasoning.stderr
+++ b/src/test/ui/impl-trait/negative-reasoning.stderr
@@ -1,3 +1,14 @@
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
+  --> $DIR/negative-reasoning.rs:19:1
+   |
+LL | impl<T: std::fmt::Debug> AnotherTrait for T {}
+   | ------------------------------------------- first implementation here
+...
+LL | impl AnotherTrait for D<OpaqueType> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
+   |
+   = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `OpaqueType` in future versions
+
 error: cannot implement trait on type alias impl trait
   --> $DIR/negative-reasoning.rs:19:25
    |
@@ -10,17 +21,6 @@ note: type alias impl trait defined here
 LL | type OpaqueType = impl OpaqueTrait;
    |                   ^^^^^^^^^^^^^^^^
 
-error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<OpaqueType>`
-  --> $DIR/negative-reasoning.rs:19:1
-   |
-LL | impl<T: std::fmt::Debug> AnotherTrait for T {}
-   | ------------------------------------------- first implementation here
-...
-LL | impl AnotherTrait for D<OpaqueType> {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<OpaqueType>`
-   |
-   = note: upstream crates may add a new impl of trait `std::fmt::Debug` for type `OpaqueType` in future versions
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/issues/issue-47511.rs b/src/test/ui/issues/issue-47511.rs
index 98c141b6c6a..eb4860e75d7 100644
--- a/src/test/ui/issues/issue-47511.rs
+++ b/src/test/ui/issues/issue-47511.rs
@@ -1,5 +1,5 @@
 // check-fail
-// known-bug
+// known-bug: #47511
 
 // Regression test for #47511: anonymous lifetimes can appear
 // unconstrained in a return type, but only if they appear just once
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs
index c79c1daf774..0e0d604ae04 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.rs
@@ -20,43 +20,43 @@ fn main() {
 
     // AcqRel is always forbidden as a failure ordering
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release is always forbidden as a failure ordering
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release success order forbids failure order of Acquire or SeqCst
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
 
     // Relaxed success order also forbids failure order of Acquire or SeqCst
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
 
     // Acquire/AcqRel forbids failure order of SeqCst
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
-    //~^ ERROR compare_exchange_weak's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange_weak`'s success ordering must be at least as strong as
 }
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr
index 13350ab0b9c..d5e53418b6f 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange-weak.stderr
@@ -1,131 +1,137 @@
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:22:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Relaxed, Ordering::AcqRel);
-   |                                                                   ^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
    = note: `#[deny(invalid_atomic_ordering)]` on by default
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:24:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::AcqRel);
-   |                                                                   ^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:26:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::AcqRel);
-   |                                                                   ^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:28:66
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::AcqRel);
-   |                                                                  ^^^^^^^^^^^^^^^^
+   |                                                                  ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:30:66
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::SeqCst, Ordering::AcqRel);
-   |                                                                  ^^^^^^^^^^^^^^^^
+   |                                                                  ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:34:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Release);
-   |                                                                   ^^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:36:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Acquire, Ordering::Release);
-   |                                                                   ^^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:38:67
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Release, Ordering::Release);
-   |                                                                   ^^^^^^^^^^^^^^^^^
+   |                                                                   ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:40:66
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::AcqRel, Ordering::Release);
-   |                                                                  ^^^^^^^^^^^^^^^^^
+   |                                                                  ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange_weak`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange_weak` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:42:66
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::SeqCst, Ordering::Release);
-   |                                                                  ^^^^^^^^^^^^^^^^^
+   |                                                                  ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:46:67
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:46:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::Acquire);
-   |                                                                   ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                                                |
+   |                                                `Release` success ordering
+   |                                                help: consider using `AcqRel` success ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:48:67
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:48:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Release, Ordering::SeqCst);
-   |                                                                   ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                                |
+   |                                                `Release` success ordering
+   |                                                help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:52:67
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:52:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::SeqCst);
-   |                                                                   ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                                |
+   |                                                `Relaxed` success ordering
+   |                                                help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:54:67
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:54:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr, ptr2, Ordering::Relaxed, Ordering::Acquire);
-   |                                                                   ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                                                |
+   |                                                `Relaxed` success ordering
+   |                                                help: consider using `Acquire` success ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `Acquire`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:58:67
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:58:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::Acquire, Ordering::SeqCst);
-   |                                                                   ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                                |
+   |                                                `Acquire` success ordering
+   |                                                help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange_weak's failure ordering may not be stronger than the success ordering of `AcqRel`
-  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:60:66
+error: `compare_exchange_weak`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange-weak.rs:60:48
    |
 LL |     let _ = x.compare_exchange_weak(ptr2, ptr, Ordering::AcqRel, Ordering::SeqCst);
-   |                                                                  ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                                                ^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                                |
+   |                                                `AcqRel` success ordering
+   |                                                help: consider using `SeqCst` success ordering instead
 
 error: aborting due to 16 previous errors
 
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs
index 8ef3a400cf0..da98d854262 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.rs
@@ -18,43 +18,43 @@ fn main() {
 
     // AcqRel is always forbidden as a failure ordering
     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release is always forbidden as a failure ordering
     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
-    //~^ ERROR compare_exchange's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release success order forbids failure order of Acquire or SeqCst
     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
 
     // Relaxed success order also forbids failure order of Acquire or SeqCst
     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
 
     // Acquire/AcqRel forbids failure order of SeqCst
     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
-    //~^ ERROR compare_exchange's failure ordering may not be stronger
+    //~^ ERROR `compare_exchange`'s success ordering must be at least as strong as
 }
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr
index daedfec7430..41121a20dee 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-exchange.stderr
@@ -1,131 +1,137 @@
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:20:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::AcqRel);
-   |                                                         ^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
    = note: `#[deny(invalid_atomic_ordering)]` on by default
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:22:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::AcqRel);
-   |                                                         ^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:24:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::AcqRel);
-   |                                                         ^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:26:56
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::AcqRel);
-   |                                                        ^^^^^^^^^^^^^^^^
+   |                                                        ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:28:56
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::AcqRel);
-   |                                                        ^^^^^^^^^^^^^^^^
+   |                                                        ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:32:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Release);
-   |                                                         ^^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:34:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::Release);
-   |                                                         ^^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:36:57
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Release);
-   |                                                         ^^^^^^^^^^^^^^^^^
+   |                                                         ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:38:56
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::Release);
-   |                                                        ^^^^^^^^^^^^^^^^^
+   |                                                        ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be `Release` or `AcqRel`
+error: `compare_exchange`'s failure ordering may not be `Release` or `AcqRel`, since a failed `compare_exchange` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-exchange.rs:40:56
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::SeqCst, Ordering::Release);
-   |                                                        ^^^^^^^^^^^^^^^^^
+   |                                                        ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:44:57
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:44:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::Acquire);
-   |                                                         ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                                      |
+   |                                      `Release` success ordering
+   |                                      help: consider using `AcqRel` success ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:46:57
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:46:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Release, Ordering::SeqCst);
-   |                                                         ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                      |
+   |                                      `Release` success ordering
+   |                                      help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:50:57
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:50:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::SeqCst);
-   |                                                         ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                      |
+   |                                      `Relaxed` success ordering
+   |                                      help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:52:57
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:52:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Relaxed, Ordering::Acquire);
-   |                                                         ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                                      |
+   |                                      `Relaxed` success ordering
+   |                                      help: consider using `Acquire` success ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `Acquire`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:56:57
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:56:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::Acquire, Ordering::SeqCst);
-   |                                                         ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                      |
+   |                                      `Acquire` success ordering
+   |                                      help: consider using `SeqCst` success ordering instead
 
-error: compare_exchange's failure ordering may not be stronger than the success ordering of `AcqRel`
-  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:58:56
+error: `compare_exchange`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-exchange.rs:58:38
    |
 LL |     let _ = x.compare_exchange(0, 0, Ordering::AcqRel, Ordering::SeqCst);
-   |                                                        ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                                      ^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                                      |
+   |                                      `AcqRel` success ordering
+   |                                      help: consider using `SeqCst` success ordering instead
 
 error: aborting due to 16 previous errors
 
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs
index 938ca0359f8..73eda182aa8 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.rs
@@ -18,43 +18,43 @@ fn main() {
 
     // AcqRel is always forbidden as a failure ordering
     let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release is always forbidden as a failure ordering
     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
     let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be `Release` or `AcqRel`
+    //~^ ERROR `fetch_update`'s failure ordering may not be `Release` or `AcqRel`
 
     // Release success order forbids failure order of Acquire or SeqCst
     let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
     let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
 
     // Relaxed success order also forbids failure order of Acquire or SeqCst
     let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
 
     // Acquire/AcqRel forbids failure order of SeqCst
     let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
     let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
-    //~^ ERROR fetch_update's failure ordering may not be stronger
+    //~^ ERROR `fetch_update`'s success ordering must be at least as strong as
 }
diff --git a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr
index dabc1da7e55..7bea56d57fb 100644
--- a/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr
+++ b/src/test/ui/lint/lint-invalid-atomic-ordering-fetch-update.stderr
@@ -1,131 +1,137 @@
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:20:47
    |
 LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::AcqRel, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
    = note: `#[deny(invalid_atomic_ordering)]` on by default
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:22:47
    |
 LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::AcqRel, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:24:47
    |
 LL |     let _ = x.fetch_update(Ordering::Release, Ordering::AcqRel, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:26:46
    |
 LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::AcqRel, |old| Some(old + 1));
-   |                                              ^^^^^^^^^^^^^^^^
+   |                                              ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:28:46
    |
 LL |     let _ = x.fetch_update(Ordering::SeqCst, Ordering::AcqRel, |old| Some(old + 1));
-   |                                              ^^^^^^^^^^^^^^^^
+   |                                              ^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:32:47
    |
 LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Release, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:34:47
    |
 LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::Release, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:36:47
    |
 LL |     let _ = x.fetch_update(Ordering::Release, Ordering::Release, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^^
+   |                                               ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering mode `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:38:46
    |
 LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::Release, |old| Some(old + 1));
-   |                                              ^^^^^^^^^^^^^^^^^
+   |                                              ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be `Release` or `AcqRel`
+error: `fetch_update`'s failure ordering may not be `Release` or `AcqRel`, since a failed `fetch_update` does not result in a write
   --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:40:46
    |
 LL |     let _ = x.fetch_update(Ordering::SeqCst, Ordering::Release, |old| Some(old + 1));
-   |                                              ^^^^^^^^^^^^^^^^^
+   |                                              ^^^^^^^^^^^^^^^^^ invalid failure ordering
    |
-   = help: consider using ordering modes `Acquire`, `SeqCst` or `Relaxed` instead
+   = help: consider using `Acquire` or `Relaxed` failure ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:44:47
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:44:28
    |
 LL |     let _ = x.fetch_update(Ordering::Release, Ordering::Acquire, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                            |
+   |                            `Release` success ordering
+   |                            help: consider using `AcqRel` success ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `Release`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:46:47
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:46:28
    |
 LL |     let _ = x.fetch_update(Ordering::Release, Ordering::SeqCst, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                            |
+   |                            `Release` success ordering
+   |                            help: consider using `SeqCst` success ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:50:47
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:50:28
    |
 LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::SeqCst, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                            |
+   |                            `Relaxed` success ordering
+   |                            help: consider using `SeqCst` success ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `Relaxed`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:52:47
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:52:28
    |
 LL |     let _ = x.fetch_update(Ordering::Relaxed, Ordering::Acquire, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering mode `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^^  ----------------- `Acquire` failure ordering
+   |                            |
+   |                            `Relaxed` success ordering
+   |                            help: consider using `Acquire` success ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `Acquire`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:56:47
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:56:28
    |
 LL |     let _ = x.fetch_update(Ordering::Acquire, Ordering::SeqCst, |old| Some(old + 1));
-   |                                               ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                            |
+   |                            `Acquire` success ordering
+   |                            help: consider using `SeqCst` success ordering instead
 
-error: fetch_update's failure ordering may not be stronger than the success ordering of `AcqRel`
-  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:58:46
+error: `fetch_update`'s success ordering must be at least as strong as its failure ordering
+  --> $DIR/lint-invalid-atomic-ordering-fetch-update.rs:58:28
    |
 LL |     let _ = x.fetch_update(Ordering::AcqRel, Ordering::SeqCst, |old| Some(old + 1));
-   |                                              ^^^^^^^^^^^^^^^^
-   |
-   = help: consider using ordering modes `Acquire` or `Relaxed` instead
+   |                            ^^^^^^^^^^^^^^^^  ---------------- `SeqCst` failure ordering
+   |                            |
+   |                            `AcqRel` success ordering
+   |                            help: consider using `SeqCst` success ordering instead
 
 error: aborting due to 16 previous errors
 
diff --git a/src/test/ui/trait-bounds/select-param-env-instead-of-blanket.rs b/src/test/ui/trait-bounds/select-param-env-instead-of-blanket.rs
index 288b2098b4c..fd975aaaee4 100644
--- a/src/test/ui/trait-bounds/select-param-env-instead-of-blanket.rs
+++ b/src/test/ui/trait-bounds/select-param-env-instead-of-blanket.rs
@@ -1,4 +1,4 @@
-// known-bug
+// known-bug: #93008
 // build-fail
 // failure-status: 101
 // compile-flags:--crate-type=lib -Zmir-opt-level=3
diff --git a/src/test/ui/traits/alias/issue-83613.stderr b/src/test/ui/traits/alias/issue-83613.stderr
index bbc240b6aec..b9d93160192 100644
--- a/src/test/ui/traits/alias/issue-83613.stderr
+++ b/src/test/ui/traits/alias/issue-83613.stderr
@@ -1,3 +1,11 @@
+error[E0119]: conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
+  --> $DIR/issue-83613.rs:10:1
+   |
+LL | impl<T: Send> AnotherTrait for T {}
+   | -------------------------------- first implementation here
+LL | impl AnotherTrait for OpaqueType {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `OpaqueType`
+
 error: cannot implement trait on type alias impl trait
   --> $DIR/issue-83613.rs:10:23
    |
@@ -10,14 +18,6 @@ note: type alias impl trait defined here
 LL | type OpaqueType = impl OpaqueTrait;
    |                   ^^^^^^^^^^^^^^^^
 
-error[E0119]: conflicting implementations of trait `AnotherTrait` for type `OpaqueType`
-  --> $DIR/issue-83613.rs:10:1
-   |
-LL | impl<T: Send> AnotherTrait for T {}
-   | -------------------------------- first implementation here
-LL | impl AnotherTrait for OpaqueType {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `OpaqueType`
-
 error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0119`.
diff --git a/src/test/ui/traits/issue-78372.rs b/src/test/ui/traits/issue-78372.rs
index 77a8c92c81c..92f9f4b467a 100644
--- a/src/test/ui/traits/issue-78372.rs
+++ b/src/test/ui/traits/issue-78372.rs
@@ -4,7 +4,6 @@ impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {} //~ ERROR cannot find type `U`
 //~^ ERROR cannot find type `MISC` in this scope
 //~| ERROR use of unstable library feature 'dispatch_from_dyn'
 //~| ERROR the trait `DispatchFromDyn` may only be implemented for a coercion between structures
-//~| ERROR type parameter `T` must be covered by another type when it appears before the first
 trait Foo: X<u32> {}
 trait X<T> {
     fn foo(self: Smaht<Self, T>);
diff --git a/src/test/ui/traits/issue-78372.stderr b/src/test/ui/traits/issue-78372.stderr
index 49a9f479368..7574c9107d9 100644
--- a/src/test/ui/traits/issue-78372.stderr
+++ b/src/test/ui/traits/issue-78372.stderr
@@ -50,22 +50,13 @@ LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
    |
    = help: add `#![feature(dispatch_from_dyn)]` to the crate attributes to enable
 
-error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Smaht<[type error], [type error]>`)
-  --> $DIR/issue-78372.rs:3:6
-   |
-LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
-   |      ^ type parameter `T` must be covered by another type when it appears before the first local type (`Smaht<[type error], [type error]>`)
-   |
-   = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
-   = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
-
 error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
   --> $DIR/issue-78372.rs:3:1
    |
 LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: aborting due to 6 previous errors
 
-Some errors have detailed explanations: E0210, E0378, E0412, E0658.
-For more information about an error, try `rustc --explain E0210`.
+Some errors have detailed explanations: E0378, E0412, E0658.
+For more information about an error, try `rustc --explain E0378`.
diff --git a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs
index 179f525de52..328096d44b4 100644
--- a/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs
+++ b/src/test/ui/type-alias-impl-trait/cross_inference_pattern_bug_no_type.rs
@@ -1,4 +1,4 @@
-// known-bug
+// known-bug: #96572
 // compile-flags: --edition=2021 --crate-type=lib
 // rustc-env:RUST_BACKTRACE=0
 
diff --git a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs
index 9c4e6c5496f..067ed7ea1e5 100644
--- a/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs
+++ b/src/test/ui/type-alias-impl-trait/issue-57611-trait-alias.rs
@@ -1,7 +1,7 @@
 // Regression test for issue #57611
 // Ensures that we don't ICE
 // FIXME: This should compile, but it currently doesn't
-// known-bug
+// known-bug: unknown
 
 #![feature(trait_alias)]
 #![feature(type_alias_impl_trait)]
diff --git a/src/test/ui/union/issue-81199.stderr b/src/test/ui/union/issue-81199.stderr
index f26bfe3a0b0..5bb98675361 100644
--- a/src/test/ui/union/issue-81199.stderr
+++ b/src/test/ui/union/issue-81199.stderr
@@ -1,28 +1,18 @@
-error[E0277]: the trait bound `T: Pointee` is not satisfied in `PtrComponents<T>`
+error[E0277]: the trait bound `T: Pointee` is not satisfied
   --> $DIR/issue-81199.rs:5:17
    |
 LL |     components: PtrComponents<T>,
-   |                 ^^^^^^^^^^^^^^^^ within `PtrComponents<T>`, the trait `Pointee` is not implemented for `T`
+   |                 ^^^^^^^^^^^^^^^^ the trait `Pointee` is not implemented for `T`
    |
-note: required because it appears within the type `PtrComponents<T>`
-  --> $DIR/issue-81199.rs:10:8
+note: required by a bound in `PtrComponents`
+  --> $DIR/issue-81199.rs:10:25
    |
 LL | struct PtrComponents<T: Pointee + ?Sized> {
-   |        ^^^^^^^^^^^^^
-   = note: no field of a union may have a dynamically sized type
-   = help: change the field's type to have a statically known size
+   |                         ^^^^^^^ required by this bound in `PtrComponents`
 help: consider further restricting this bound
    |
 LL | union PtrRepr<T: ?Sized + Pointee> {
    |                         +++++++++
-help: borrowed types always have a statically known size
-   |
-LL |     components: &PtrComponents<T>,
-   |                 +
-help: the `Box` type always has a statically known size and allocates its contents in the heap
-   |
-LL |     components: Box<PtrComponents<T>>,
-   |                 ++++                +
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/wf/issue-96810.rs b/src/test/ui/wf/issue-96810.rs
new file mode 100644
index 00000000000..c2948086b20
--- /dev/null
+++ b/src/test/ui/wf/issue-96810.rs
@@ -0,0 +1,12 @@
+struct S<T: Tr>(T::Assoc);
+
+trait Tr {
+    type Assoc;
+}
+
+struct Hoge<K> {
+    s: S<K>, //~ ERROR the trait bound `K: Tr` is not satisfied
+    a: u32,
+}
+
+fn main() {}
diff --git a/src/test/ui/wf/issue-96810.stderr b/src/test/ui/wf/issue-96810.stderr
new file mode 100644
index 00000000000..1407e62b1e1
--- /dev/null
+++ b/src/test/ui/wf/issue-96810.stderr
@@ -0,0 +1,19 @@
+error[E0277]: the trait bound `K: Tr` is not satisfied
+  --> $DIR/issue-96810.rs:8:8
+   |
+LL |     s: S<K>,
+   |        ^^^^ the trait `Tr` is not implemented for `K`
+   |
+note: required by a bound in `S`
+  --> $DIR/issue-96810.rs:1:13
+   |
+LL | struct S<T: Tr>(T::Assoc);
+   |             ^^ required by this bound in `S`
+help: consider restricting type parameter `K`
+   |
+LL | struct Hoge<K: Tr> {
+   |              ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 5352f7c6fe0..31e979a574b 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -395,7 +395,29 @@ impl TestProps {
                 );
                 config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth);
                 config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
-                config.set_name_directive(ln, KNOWN_BUG, &mut self.known_bug);
+
+                // Unlike the other `name_value_directive`s this needs to be handled manually,
+                // because it sets a `bool` flag.
+                if let Some(known_bug) = config.parse_name_value_directive(ln, KNOWN_BUG) {
+                    let known_bug = known_bug.trim();
+                    if known_bug == "unknown"
+                        || known_bug.split(',').all(|issue_ref| {
+                            issue_ref
+                                .trim()
+                                .split_once('#')
+                                .filter(|(_, number)| {
+                                    number.chars().all(|digit| digit.is_numeric())
+                                })
+                                .is_some()
+                        })
+                    {
+                        self.known_bug = true;
+                    } else {
+                        panic!(
+                            "Invalid known-bug value: {known_bug}\nIt requires comma-separated issue references (`#000` or `chalk#000`) or `unknown`."
+                        );
+                    }
+                }
                 config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| {
                     s.trim().to_string()
                 });