about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_arena/src/lib.rs41
-rw-r--r--compiler/rustc_ast_lowering/src/asm.rs121
-rw-r--r--compiler/rustc_codegen_ssa/Cargo.toml2
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs22
-rw-r--r--compiler/rustc_const_eval/src/interpret/machine.rs29
-rw-r--r--compiler/rustc_const_eval/src/interpret/memory.rs9
-rw-r--r--compiler/rustc_const_eval/src/interpret/projection.rs12
-rw-r--r--compiler/rustc_data_structures/Cargo.toml2
-rw-r--r--compiler/rustc_data_structures/src/jobserver.rs96
-rw-r--r--compiler/rustc_hir_typeck/src/coercion.rs99
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/usefulness.rs23
-rw-r--r--compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs12
-rw-r--r--compiler/rustc_parse/src/parser/diagnostics.rs16
-rw-r--r--compiler/rustc_session/src/code_stats.rs4
-rw-r--r--compiler/rustc_session/src/lib.rs1
-rw-r--r--compiler/rustc_session/src/parse.rs3
-rw-r--r--compiler/rustc_session/src/session.rs17
-rw-r--r--compiler/rustc_trait_selection/messages.ftl3
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs147
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs11
20 files changed, 437 insertions, 233 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index bd35364509a..756af7269f2 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -197,23 +197,24 @@ impl<T> TypedArena<T> {
         start_ptr
     }
 
+    /// Allocates the elements of this iterator into a contiguous slice in the `TypedArena`.
+    ///
+    /// Note: for reasons of reentrancy and panic safety we collect into a `SmallVec<[_; 8]>` before
+    /// storing the elements in the arena.
     #[inline]
     pub fn alloc_from_iter<I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
-        // This implementation is entirely separate to
-        // `DroplessIterator::alloc_from_iter`, even though conceptually they
-        // are the same.
+        // Despite the similarlty with `DroplessArena`, we cannot reuse their fast case. The reason
+        // is subtle: these arenas are reentrant. In other words, `iter` may very well be holding a
+        // reference to `self` and adding elements to the arena during iteration.
         //
-        // `DroplessIterator` (in the fast case) writes elements from the
-        // iterator one at a time into the allocated memory. That's easy
-        // because the elements don't implement `Drop`. But for `TypedArena`
-        // they do implement `Drop`, which means that if the iterator panics we
-        // could end up with some allocated-but-uninitialized elements, which
-        // will then cause UB in `TypedArena::drop`.
+        // For this reason, if we pre-allocated any space for the elements of this iterator, we'd
+        // have to track that some uninitialized elements are followed by some initialized elements,
+        // else we might accidentally drop uninitialized memory if something panics or if the
+        // iterator doesn't fill all the length we expected.
         //
-        // Instead we use an approach where any iterator panic will occur
-        // before the memory is allocated. This function is much less hot than
-        // `DroplessArena::alloc_from_iter`, so it doesn't need to be
-        // hyper-optimized.
+        // So we collect all the elements beforehand, which takes care of reentrancy and panic
+        // safety. This function is much less hot than `DroplessArena::alloc_from_iter`, so it
+        // doesn't need to be hyper-optimized.
         assert!(mem::size_of::<T>() != 0);
 
         let mut vec: SmallVec<[_; 8]> = iter.into_iter().collect();
@@ -485,8 +486,9 @@ impl DroplessArena {
 
     /// # Safety
     ///
-    /// The caller must ensure that `mem` is valid for writes up to
-    /// `size_of::<T>() * len`.
+    /// The caller must ensure that `mem` is valid for writes up to `size_of::<T>() * len`, and that
+    /// that memory stays allocated and not shared for the lifetime of `self`. This must hold even
+    /// if `iter.next()` allocates onto `self`.
     #[inline]
     unsafe fn write_from_iter<T, I: Iterator<Item = T>>(
         &self,
@@ -516,6 +518,8 @@ impl DroplessArena {
 
     #[inline]
     pub fn alloc_from_iter<T, I: IntoIterator<Item = T>>(&self, iter: I) -> &mut [T] {
+        // Warning: this function is reentrant: `iter` could hold a reference to `&self` and
+        // allocate additional elements while we're iterating.
         let iter = iter.into_iter();
         assert!(mem::size_of::<T>() != 0);
         assert!(!mem::needs_drop::<T>());
@@ -524,7 +528,7 @@ impl DroplessArena {
 
         match size_hint {
             (min, Some(max)) if min == max => {
-                // We know the exact number of elements the iterator will produce here
+                // We know the exact number of elements the iterator expects to produce here.
                 let len = min;
 
                 if len == 0 {
@@ -532,10 +536,15 @@ impl DroplessArena {
                 }
 
                 let mem = self.alloc_raw(Layout::array::<T>(len).unwrap()) as *mut T;
+                // SAFETY: `write_from_iter` doesn't touch `self`. It only touches the slice we just
+                // reserved. If the iterator panics or doesn't output `len` elements, this will
+                // leave some unallocated slots in the arena, which is fine because we do not call
+                // `drop`.
                 unsafe { self.write_from_iter(iter, len, mem) }
             }
             (_, _) => {
                 outline(move || -> &mut [T] {
+                    // Takes care of reentrancy.
                     let mut vec: SmallVec<[_; 8]> = iter.collect();
                     if vec.is_empty() {
                         return &mut [];
diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs
index 6ac6fe7ebc9..b75a686b819 100644
--- a/compiler/rustc_ast_lowering/src/asm.rs
+++ b/compiler/rustc_ast_lowering/src/asm.rs
@@ -342,70 +342,75 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
                     // Flag to output the error only once per operand
                     let mut skip = false;
-                    reg.overlapping_regs(|r| {
-                        let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
-                                         input| {
-                            match used_regs.entry(r) {
-                                Entry::Occupied(o) => {
-                                    if skip {
-                                        return;
-                                    }
-                                    skip = true;
-
-                                    let idx2 = *o.get();
-                                    let (ref op2, op_sp2) = operands[idx2];
 
-                                    let in_out = match (op, op2) {
-                                        (
-                                            hir::InlineAsmOperand::In { .. },
-                                            hir::InlineAsmOperand::Out { late, .. },
-                                        )
-                                        | (
-                                            hir::InlineAsmOperand::Out { late, .. },
-                                            hir::InlineAsmOperand::In { .. },
-                                        ) => {
-                                            assert!(!*late);
-                                            let out_op_sp = if input { op_sp2 } else { op_sp };
-                                            Some(out_op_sp)
-                                        }
-                                        _ => None,
-                                    };
+                    let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
+                                     input,
+                                     r: asm::InlineAsmReg| {
+                        match used_regs.entry(r) {
+                            Entry::Occupied(o) => {
+                                if skip {
+                                    return;
+                                }
+                                skip = true;
 
-                                    let reg_str = |idx| -> &str {
-                                        // HIR asm doesn't preserve the original alias string of the explicit register,
-                                        // so we have to retrieve it from AST
-                                        let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
-                                        if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) =
-                                            op.reg()
-                                        {
-                                            reg_sym.as_str()
-                                        } else {
-                                            unreachable!();
-                                        }
-                                    };
+                                let idx2 = *o.get();
+                                let (ref op2, op_sp2) = operands[idx2];
 
-                                    sess.emit_err(RegisterConflict {
-                                        op_span1: op_sp,
-                                        op_span2: op_sp2,
-                                        reg1_name: reg_str(idx),
-                                        reg2_name: reg_str(idx2),
-                                        in_out,
-                                    });
-                                }
-                                Entry::Vacant(v) => {
-                                    if r == reg {
-                                        v.insert(idx);
+                                let in_out = match (op, op2) {
+                                    (
+                                        hir::InlineAsmOperand::In { .. },
+                                        hir::InlineAsmOperand::Out { late, .. },
+                                    )
+                                    | (
+                                        hir::InlineAsmOperand::Out { late, .. },
+                                        hir::InlineAsmOperand::In { .. },
+                                    ) => {
+                                        assert!(!*late);
+                                        let out_op_sp = if input { op_sp2 } else { op_sp };
+                                        Some(out_op_sp)
+                                    }
+                                    _ => None,
+                                };
+                                let reg_str = |idx| -> &str {
+                                    // HIR asm doesn't preserve the original alias string of the explicit register,
+                                    // so we have to retrieve it from AST
+                                    let (op, _): &(InlineAsmOperand, Span) = &asm.operands[idx];
+                                    if let Some(ast::InlineAsmRegOrRegClass::Reg(reg_sym)) =
+                                        op.reg()
+                                    {
+                                        reg_sym.as_str()
+                                    } else {
+                                        unreachable!();
                                     }
+                                };
+
+                                sess.emit_err(RegisterConflict {
+                                    op_span1: op_sp,
+                                    op_span2: op_sp2,
+                                    reg1_name: reg_str(idx),
+                                    reg2_name: reg_str(idx2),
+                                    in_out,
+                                });
+                            }
+                            Entry::Vacant(v) => {
+                                if r == reg {
+                                    v.insert(idx);
                                 }
                             }
-                        };
+                        }
+                    };
+                    let mut overlapping_with = vec![];
+                    reg.overlapping_regs(|r| {
+                        overlapping_with.push(r);
+                    });
+                    for r in overlapping_with {
                         if input {
-                            check(&mut used_input_regs, true);
+                            check(&mut used_input_regs, true, r);
                         }
                         if output {
-                            check(&mut used_output_regs, false);
+                            check(&mut used_output_regs, false, r);
                         }
-                    });
+                    }
                 }
             }
         }
@@ -420,12 +425,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     continue;
                 }
 
-                let mut output_used = false;
+                let mut overlapping_with = vec![];
                 clobber.overlapping_regs(|reg| {
-                    if used_output_regs.contains_key(&reg) {
-                        output_used = true;
-                    }
+                    overlapping_with.push(reg);
                 });
+                let output_used =
+                    overlapping_with.iter().any(|reg| used_output_regs.contains_key(&reg));
 
                 if !output_used {
                     operands.push((
diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml
index 0ca19e5fe4a..3f2ed257d08 100644
--- a/compiler/rustc_codegen_ssa/Cargo.toml
+++ b/compiler/rustc_codegen_ssa/Cargo.toml
@@ -9,7 +9,7 @@ ar_archive_writer = "0.1.5"
 bitflags = "1.2.1"
 cc = "1.0.69"
 itertools = "0.11"
-jobserver = "0.1.22"
+jobserver = "0.1.27"
 pathdiff = "0.2.0"
 regex = "1.4"
 rustc_arena = { path = "../rustc_arena" }
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index 45795a7f735..83425dee1a8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -143,7 +143,8 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         // Simple cases, which don't need DST adjustment:
         //   * no metadata available - just log the case
         //   * known alignment - sized types, `[T]`, `str` or a foreign type
-        //   * packed struct - there is no alignment padding
+        // Note that looking at `field.align` is incorrect since that is not necessarily equal
+        // to the dynamic alignment of the type.
         match field.ty.kind() {
             _ if self.llextra.is_none() => {
                 debug!(
@@ -154,14 +155,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
             }
             _ if field.is_sized() => return simple(),
             ty::Slice(..) | ty::Str | ty::Foreign(..) => return simple(),
-            ty::Adt(def, _) => {
-                if def.repr().packed() {
-                    // FIXME(eddyb) generalize the adjustment when we
-                    // start supporting packing to larger alignments.
-                    assert_eq!(self.layout.align.abi.bytes(), 1);
-                    return simple();
-                }
-            }
             _ => {}
         }
 
@@ -186,7 +179,16 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
         let unaligned_offset = bx.cx().const_usize(offset.bytes());
 
         // Get the alignment of the field
-        let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
+        let (_, mut unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta);
+
+        // For packed types, we need to cap alignment.
+        if let ty::Adt(def, _) = self.layout.ty.kind()
+            && let Some(packed) = def.repr().pack
+        {
+            let packed = bx.const_usize(packed.bytes());
+            let cmp = bx.icmp(IntPredicate::IntULT, unsized_align, packed);
+            unsized_align = bx.select(cmp, unsized_align, packed)
+        }
 
         // Bump the unaligned offset up to the appropriate alignment
         let offset = round_up_const_value_to_alignment(bx, unaligned_offset, unsized_align);
diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs
index 6617f6f2ffb..221ff2b60e9 100644
--- a/compiler/rustc_const_eval/src/interpret/machine.rs
+++ b/compiler/rustc_const_eval/src/interpret/machine.rs
@@ -12,12 +12,12 @@ use rustc_middle::mir;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::def_id::DefId;
-use rustc_target::abi::Size;
+use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi as CallAbi;
 
 use super::{
-    AllocBytes, AllocId, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy, InterpCx,
-    InterpResult, MPlaceTy, MemoryKind, OpTy, PlaceTy, Pointer, Provenance,
+    AllocBytes, AllocId, AllocKind, AllocRange, Allocation, ConstAllocation, FnArg, Frame, ImmTy,
+    InterpCx, InterpResult, MPlaceTy, MemoryKind, Misalignment, OpTy, PlaceTy, Pointer, Provenance,
 };
 
 /// Data returned by Machine::stack_pop,
@@ -143,11 +143,18 @@ pub trait Machine<'mir, 'tcx: 'mir>: Sized {
     /// Whether memory accesses should be alignment-checked.
     fn enforce_alignment(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
 
-    /// Whether, when checking alignment, we should look at the actual address and thus support
-    /// custom alignment logic based on whatever the integer address happens to be.
-    ///
-    /// If this returns true, Provenance::OFFSET_IS_ADDR must be true.
-    fn use_addr_for_alignment_check(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
+    /// Gives the machine a chance to detect more misalignment than the built-in checks would catch.
+    #[inline(always)]
+    fn alignment_check(
+        _ecx: &InterpCx<'mir, 'tcx, Self>,
+        _alloc_id: AllocId,
+        _alloc_align: Align,
+        _alloc_kind: AllocKind,
+        _offset: Size,
+        _align: Align,
+    ) -> Option<Misalignment> {
+        None
+    }
 
     /// Whether to enforce the validity invariant for a specific layout.
     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>, layout: TyAndLayout<'tcx>) -> bool;
@@ -520,12 +527,6 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
     type Bytes = Box<[u8]>;
 
     #[inline(always)]
-    fn use_addr_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
-        // We do not support `use_addr`.
-        false
-    }
-
-    #[inline(always)]
     fn ignore_optional_overflow_checks(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {
         false
     }
diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs
index 1c1fd2a71ba..f865c0cc5fa 100644
--- a/compiler/rustc_const_eval/src/interpret/memory.rs
+++ b/compiler/rustc_const_eval/src/interpret/memory.rs
@@ -58,6 +58,7 @@ impl<T: fmt::Display> fmt::Display for MemoryKind<T> {
 }
 
 /// The return value of `get_alloc_info` indicates the "kind" of the allocation.
+#[derive(Copy, Clone, PartialEq, Debug)]
 pub enum AllocKind {
     /// A regular live data allocation.
     LiveData,
@@ -473,8 +474,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
         match self.ptr_try_get_alloc_id(ptr) {
             Err(addr) => offset_misalignment(addr, align),
             Ok((alloc_id, offset, _prov)) => {
-                let (_size, alloc_align, _kind) = self.get_alloc_info(alloc_id);
-                if M::use_addr_for_alignment_check(self) {
+                let (_size, alloc_align, kind) = self.get_alloc_info(alloc_id);
+                if let Some(misalign) =
+                    M::alignment_check(self, alloc_id, alloc_align, kind, offset, align)
+                {
+                    Some(misalign)
+                } else if M::Provenance::OFFSET_IS_ADDR {
                     // `use_addr_for_alignment_check` can only be true if `OFFSET_IS_ADDR` is true.
                     offset_misalignment(ptr.addr().bytes(), align)
                 } else {
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index c8977aac0fc..4d9e296d544 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -163,7 +163,17 @@ where
             // With custom DSTS, this *will* execute user-defined code, but the same
             // happens at run-time so that's okay.
             match self.size_and_align_of(&base_meta, &field_layout)? {
-                Some((_, align)) => (base_meta, offset.align_to(align)),
+                Some((_, align)) => {
+                    // For packed types, we need to cap alignment.
+                    let align = if let ty::Adt(def, _) = base.layout().ty.kind()
+                        && let Some(packed) = def.repr().pack
+                    {
+                        align.min(packed)
+                    } else {
+                        align
+                    };
+                    (base_meta, offset.align_to(align))
+                }
                 None => {
                     // For unsized types with an extern type tail we perform no adjustments.
                     // NOTE: keep this in sync with `PlaceRef::project_field` in the codegen backend.
diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml
index 77d9e491748..4732783a12d 100644
--- a/compiler/rustc_data_structures/Cargo.toml
+++ b/compiler/rustc_data_structures/Cargo.toml
@@ -11,7 +11,7 @@ elsa = "=1.7.1"
 ena = "0.14.2"
 indexmap = { version = "2.0.0" }
 itertools = "0.11"
-jobserver_crate = { version = "0.1.13", package = "jobserver" }
+jobserver_crate = { version = "0.1.27", package = "jobserver" }
 libc = "0.2"
 measureme = "10.0.0"
 rustc-hash = "1.1.0"
diff --git a/compiler/rustc_data_structures/src/jobserver.rs b/compiler/rustc_data_structures/src/jobserver.rs
index 09baa3095a4..b777bfd4d3c 100644
--- a/compiler/rustc_data_structures/src/jobserver.rs
+++ b/compiler/rustc_data_structures/src/jobserver.rs
@@ -1,40 +1,78 @@
 pub use jobserver_crate::Client;
-use std::sync::LazyLock;
-
-// We can only call `from_env` once per process
-
-// Note that this is unsafe because it may misinterpret file descriptors
-// on Unix as jobserver file descriptors. We hopefully execute this near
-// the beginning of the process though to ensure we don't get false
-// positives, or in other words we try to execute this before we open
-// any file descriptors ourselves.
-//
-// Pick a "reasonable maximum" if we don't otherwise have
-// a jobserver in our environment, capping out at 32 so we
-// don't take everything down by hogging the process run queue.
-// The fixed number is used to have deterministic compilation
-// across machines.
-//
-// Also note that we stick this in a global because there could be
-// multiple rustc instances in this process, and the jobserver is
-// per-process.
-static GLOBAL_CLIENT: LazyLock<Client> = LazyLock::new(|| unsafe {
-    Client::from_env().unwrap_or_else(|| {
-        let client = Client::new(32).expect("failed to create jobserver");
-        // Acquire a token for the main thread which we can release later
-        client.acquire_raw().ok();
-        client
-    })
+
+use jobserver_crate::{FromEnv, FromEnvErrorKind};
+
+use std::sync::{LazyLock, OnceLock};
+
+// We can only call `from_env_ext` once per process
+
+// We stick this in a global because there could be multiple rustc instances
+// in this process, and the jobserver is per-process.
+static GLOBAL_CLIENT: LazyLock<Result<Client, String>> = LazyLock::new(|| {
+    // Note that this is unsafe because it may misinterpret file descriptors
+    // on Unix as jobserver file descriptors. We hopefully execute this near
+    // the beginning of the process though to ensure we don't get false
+    // positives, or in other words we try to execute this before we open
+    // any file descriptors ourselves.
+    let FromEnv { client, var } = unsafe { Client::from_env_ext(true) };
+
+    let error = match client {
+        Ok(client) => return Ok(client),
+        Err(e) => e,
+    };
+
+    if matches!(
+        error.kind(),
+        FromEnvErrorKind::NoEnvVar | FromEnvErrorKind::NoJobserver | FromEnvErrorKind::Unsupported
+    ) {
+        return Ok(default_client());
+    }
+
+    // Environment specifies jobserver, but it looks incorrect.
+    // Safety: `error.kind()` should be `NoEnvVar` if `var == None`.
+    let (name, value) = var.unwrap();
+    Err(format!(
+        "failed to connect to jobserver from environment variable `{name}={:?}`: {error}",
+        value
+    ))
 });
 
+// Create a new jobserver if there's no inherited one.
+fn default_client() -> Client {
+    // Pick a "reasonable maximum" capping out at 32
+    // so we don't take everything down by hogging the process run queue.
+    // The fixed number is used to have deterministic compilation across machines.
+    let client = Client::new(32).expect("failed to create jobserver");
+
+    // Acquire a token for the main thread which we can release later
+    client.acquire_raw().ok();
+
+    client
+}
+
+static GLOBAL_CLIENT_CHECKED: OnceLock<Client> = OnceLock::new();
+
+pub fn check(report_warning: impl FnOnce(&'static str)) {
+    let client_checked = match &*GLOBAL_CLIENT {
+        Ok(client) => client.clone(),
+        Err(e) => {
+            report_warning(e);
+            default_client()
+        }
+    };
+    GLOBAL_CLIENT_CHECKED.set(client_checked).ok();
+}
+
+const ACCESS_ERROR: &str = "jobserver check should have been called earlier";
+
 pub fn client() -> Client {
-    GLOBAL_CLIENT.clone()
+    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).clone()
 }
 
 pub fn acquire_thread() {
-    GLOBAL_CLIENT.acquire_raw().ok();
+    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).acquire_raw().ok();
 }
 
 pub fn release_thread() {
-    GLOBAL_CLIENT.release_raw().ok();
+    GLOBAL_CLIENT_CHECKED.get().expect(ACCESS_ERROR).release_raw().ok();
 }
diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs
index f39d795f0ed..7f56b3850dd 100644
--- a/compiler/rustc_hir_typeck/src/coercion.rs
+++ b/compiler/rustc_hir_typeck/src/coercion.rs
@@ -36,7 +36,9 @@
 //! ```
 
 use crate::FnCtxt;
-use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
+use rustc_errors::{
+    struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
+};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, Visitor};
@@ -53,8 +55,7 @@ use rustc_middle::ty::adjustment::{
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::relate::RelateResult;
 use rustc_middle::ty::visit::TypeVisitableExt;
-use rustc_middle::ty::GenericArgsRef;
-use rustc_middle::ty::{self, Ty, TypeAndMut};
+use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeAndMut};
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
 use rustc_span::{self, DesugaringKind};
@@ -1639,12 +1640,15 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                         None,
                         Some(coercion_error),
                     );
-                }
-
-                if visitor.ret_exprs.len() > 0
-                    && let Some(expr) = expression
-                {
-                    self.note_unreachable_loop_return(&mut err, expr, &visitor.ret_exprs);
+                    if visitor.ret_exprs.len() > 0 {
+                        self.note_unreachable_loop_return(
+                            &mut err,
+                            fcx.tcx,
+                            &expr,
+                            &visitor.ret_exprs,
+                            expected,
+                        );
+                    }
                 }
 
                 let reported = err.emit_unless(unsized_return);
@@ -1657,8 +1661,10 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
     fn note_unreachable_loop_return(
         &self,
         err: &mut Diagnostic,
+        tcx: TyCtxt<'tcx>,
         expr: &hir::Expr<'tcx>,
         ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>,
+        ty: Ty<'tcx>,
     ) {
         let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else {
             return;
@@ -1683,10 +1689,77 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
                 ret_exprs.len() - MAXITER
             ));
         }
-        err.help(
-            "return a value for the case when the loop has zero elements to iterate on, or \
-           consider changing the return type to account for that possibility",
-        );
+        let hir = tcx.hir();
+        let item = hir.get_parent_item(expr.hir_id);
+        let ret_msg = "return a value for the case when the loop has zero elements to iterate on";
+        let ret_ty_msg =
+            "otherwise consider changing the return type to account for that possibility";
+        if let Some(node) = hir.find(item.into())
+            && let Some(body_id) = node.body_id()
+            && let Some(sig) = node.fn_sig()
+            && let hir::ExprKind::Block(block, _) = hir.body(body_id).value.kind
+            && !ty.is_never()
+        {
+            let indentation = if let None = block.expr
+                && let [.., last] = &block.stmts[..]
+            {
+                tcx.sess.source_map().indentation_before(last.span).unwrap_or_else(String::new)
+            } else if let Some(expr) = block.expr {
+                tcx.sess.source_map().indentation_before(expr.span).unwrap_or_else(String::new)
+            } else {
+                String::new()
+            };
+            if let None = block.expr
+                && let [.., last] = &block.stmts[..]
+            {
+                err.span_suggestion_verbose(
+                    last.span.shrink_to_hi(),
+                    ret_msg,
+                    format!("\n{indentation}/* `{ty}` value */"),
+                    Applicability::MaybeIncorrect,
+                );
+            } else if let Some(expr) = block.expr {
+                err.span_suggestion_verbose(
+                    expr.span.shrink_to_hi(),
+                    ret_msg,
+                    format!("\n{indentation}/* `{ty}` value */"),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+            let mut sugg = match sig.decl.output {
+                hir::FnRetTy::DefaultReturn(span) => {
+                    vec![(span, " -> Option<()>".to_string())]
+                }
+                hir::FnRetTy::Return(ty) => {
+                    vec![
+                        (ty.span.shrink_to_lo(), "Option<".to_string()),
+                        (ty.span.shrink_to_hi(), ">".to_string()),
+                    ]
+                }
+            };
+            for ret_expr in ret_exprs {
+                match ret_expr.kind {
+                    hir::ExprKind::Ret(Some(expr)) => {
+                        sugg.push((expr.span.shrink_to_lo(), "Some(".to_string()));
+                        sugg.push((expr.span.shrink_to_hi(), ")".to_string()));
+                    }
+                    hir::ExprKind::Ret(None) => {
+                        sugg.push((ret_expr.span.shrink_to_hi(), " Some(())".to_string()));
+                    }
+                    _ => {}
+                }
+            }
+            if let None = block.expr
+                && let [.., last] = &block.stmts[..]
+            {
+                sugg.push((last.span.shrink_to_hi(), format!("\n{indentation}None")));
+            } else if let Some(expr) = block.expr {
+                sugg.push((expr.span.shrink_to_hi(), format!("\n{indentation}None")));
+            }
+            err.multipart_suggestion(ret_ty_msg, sugg, Applicability::MaybeIncorrect);
+        } else {
+            err.help(format!("{ret_msg}, {ret_ty_msg}"));
+        }
     }
 
     fn report_return_mismatched_types<'a>(
diff --git a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
index 8f017833531..a2f829f93e3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/usefulness.rs
@@ -599,9 +599,9 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
     // an or-pattern. Panics if `self` is empty.
     fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
         self.head().flatten_or_pat().into_iter().map(move |pat| {
-            let mut new_pats = smallvec![pat];
-            new_pats.extend_from_slice(&self.pats[1..]);
-            PatStack { pats: new_pats }
+            let mut new = self.clone();
+            new.pats[0] = pat;
+            new
         })
     }
 
@@ -732,18 +732,11 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
     }
 
     /// Build a new matrix from an iterator of `MatchArm`s.
-    fn new<'a>(
-        cx: &MatchCheckCtxt<'p, 'tcx>,
-        iter: impl Iterator<Item = &'a MatchArm<'p, 'tcx>>,
-        scrut_ty: Ty<'tcx>,
-    ) -> Self
-    where
-        'p: 'a,
-    {
+    fn new(cx: &MatchCheckCtxt<'p, 'tcx>, arms: &[MatchArm<'p, 'tcx>], scrut_ty: Ty<'tcx>) -> Self {
         let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
         let wildcard_row = PatStack::from_pattern(wild_pattern);
-        let mut matrix = Matrix { rows: vec![], wildcard_row };
-        for (row_id, arm) in iter.enumerate() {
+        let mut matrix = Matrix { rows: Vec::with_capacity(arms.len()), wildcard_row };
+        for (row_id, arm) in arms.iter().enumerate() {
             let v = MatrixRow {
                 pats: PatStack::from_pattern(arm.pat),
                 parent_row: row_id, // dummy, we won't read it
@@ -806,7 +799,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
         ctor: &Constructor<'tcx>,
     ) -> Matrix<'p, 'tcx> {
         let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
-        let mut matrix = Matrix { rows: vec![], wildcard_row };
+        let mut matrix = Matrix { rows: Vec::new(), wildcard_row };
         for (i, row) in self.rows().enumerate() {
             if ctor.is_covered_by(pcx, row.head().ctor()) {
                 let new_row = row.pop_head_constructor(pcx, ctor, i);
@@ -1386,7 +1379,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
     arms: &[MatchArm<'p, 'tcx>],
     scrut_ty: Ty<'tcx>,
 ) -> UsefulnessReport<'p, 'tcx> {
-    let mut matrix = Matrix::new(cx, arms.iter(), scrut_ty);
+    let mut matrix = Matrix::new(cx, arms, scrut_ty);
     let non_exhaustiveness_witnesses =
         compute_exhaustiveness_and_reachability(cx, &mut matrix, true);
 
diff --git a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
index 6189e5379ea..e1531f2c239 100644
--- a/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
+++ b/compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
@@ -63,14 +63,14 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
 
         let statement_spans = data.statements.iter().filter_map(move |statement| {
             let expn_span = filtered_statement_span(statement)?;
-            let span = function_source_span(expn_span, body_span);
+            let span = unexpand_into_body_span(expn_span, body_span)?;
 
             Some(CoverageSpan::new(span, expn_span, bcb, is_closure(statement)))
         });
 
         let terminator_span = Some(data.terminator()).into_iter().filter_map(move |terminator| {
             let expn_span = filtered_terminator_span(terminator)?;
-            let span = function_source_span(expn_span, body_span);
+            let span = unexpand_into_body_span(expn_span, body_span)?;
 
             Some(CoverageSpan::new(span, expn_span, bcb, false))
         });
@@ -180,14 +180,16 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
 /// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
 /// within the function's body source. This span is guaranteed to be contained
 /// within, or equal to, the `body_span`. If the extrapolated span is not
-/// contained within the `body_span`, the `body_span` is returned.
+/// contained within the `body_span`, `None` is returned.
 ///
 /// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
 /// etc.).
 #[inline]
-fn function_source_span(span: Span, body_span: Span) -> Span {
+fn unexpand_into_body_span(span: Span, body_span: Span) -> Option<Span> {
     use rustc_span::source_map::original_sp;
 
+    // FIXME(#118525): Consider switching from `original_sp` to `Span::find_ancestor_inside`,
+    // which is similar but gives slightly different results in some edge cases.
     let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
-    if body_span.contains(original_span) { original_span } else { body_span }
+    body_span.contains(original_span).then_some(original_span)
 }
diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs
index a4eb36dd92c..98e68e682ab 100644
--- a/compiler/rustc_parse/src/parser/diagnostics.rs
+++ b/compiler/rustc_parse/src/parser/diagnostics.rs
@@ -18,7 +18,6 @@ use crate::errors::{
     UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
     UseEqInstead, WrapType,
 };
-
 use crate::fluent_generated as fluent;
 use crate::parser;
 use crate::parser::attr::InnerAttrPolicy;
@@ -772,8 +771,10 @@ impl<'a> Parser<'a> {
                 && let ast::AttrKind::Normal(attr_kind) = &attr.kind
                 && let [segment] = &attr_kind.item.path.segments[..]
                 && segment.ident.name == sym::cfg
+                && let Some(args_span) = attr_kind.item.args.span()
                 && let Ok(next_attr) = snapshot.parse_attribute(InnerAttrPolicy::Forbidden(None))
                 && let ast::AttrKind::Normal(next_attr_kind) = next_attr.kind
+                && let Some(next_attr_args_span) = next_attr_kind.item.args.span()
                 && let [next_segment] = &next_attr_kind.item.path.segments[..]
                 && segment.ident.name == sym::cfg
                 && let Ok(next_expr) = snapshot.parse_expr()
@@ -787,23 +788,14 @@ impl<'a> Parser<'a> {
                 let margin = self.sess.source_map().span_to_margin(next_expr.span).unwrap_or(0);
                 let sugg = vec![
                     (attr.span.with_hi(segment.span().hi()), "if cfg!".to_string()),
-                    (
-                        attr_kind.item.args.span().unwrap().shrink_to_hi().with_hi(attr.span.hi()),
-                        " {".to_string(),
-                    ),
+                    (args_span.shrink_to_hi().with_hi(attr.span.hi()), " {".to_string()),
                     (expr.span.shrink_to_lo(), "    ".to_string()),
                     (
                         next_attr.span.with_hi(next_segment.span().hi()),
                         "} else if cfg!".to_string(),
                     ),
                     (
-                        next_attr_kind
-                            .item
-                            .args
-                            .span()
-                            .unwrap()
-                            .shrink_to_hi()
-                            .with_hi(next_attr.span.hi()),
+                        next_attr_args_span.shrink_to_hi().with_hi(next_attr.span.hi()),
                         " {".to_string(),
                     ),
                     (next_expr.span.shrink_to_lo(), "    ".to_string()),
diff --git a/compiler/rustc_session/src/code_stats.rs b/compiler/rustc_session/src/code_stats.rs
index e1eb58fecc7..2553df33cc7 100644
--- a/compiler/rustc_session/src/code_stats.rs
+++ b/compiler/rustc_session/src/code_stats.rs
@@ -132,6 +132,8 @@ impl CodeStats {
 
     pub fn print_type_sizes(&self) {
         let type_sizes = self.type_sizes.borrow();
+        // We will soon sort, so the initial order does not matter.
+        #[allow(rustc::potential_query_instability)]
         let mut sorted: Vec<_> = type_sizes.iter().collect();
 
         // Primary sort: large-to-small.
@@ -227,6 +229,8 @@ impl CodeStats {
     }
 
     pub fn print_vtable_sizes(&self, crate_name: Symbol) {
+        // We will soon sort, so the initial order does not matter.
+        #[allow(rustc::potential_query_instability)]
         let mut infos =
             std::mem::take(&mut *self.vtable_sizes.lock()).into_values().collect::<Vec<_>>();
 
diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs
index 0b55af2f73b..805854bd5cf 100644
--- a/compiler/rustc_session/src/lib.rs
+++ b/compiler/rustc_session/src/lib.rs
@@ -6,7 +6,6 @@
 #![feature(map_many_mut)]
 #![feature(iter_intersperse)]
 #![recursion_limit = "256"]
-#![allow(rustc::potential_query_instability)]
 #![deny(rustc::untranslatable_diagnostic)]
 #![deny(rustc::diagnostic_outside_of_impl)]
 #![allow(internal_features)]
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index f7b33cb598b..881e1de6755 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -51,6 +51,9 @@ impl GatedSpans {
     /// Prepend the given set of `spans` onto the set in `self`.
     pub fn merge(&self, mut spans: FxHashMap<Symbol, Vec<Span>>) {
         let mut inner = self.spans.borrow_mut();
+        // The entries will be moved to another map so the drain order does not
+        // matter.
+        #[allow(rustc::potential_query_instability)]
         for (gate, mut gate_spans) in inner.drain() {
             spans.entry(gate).or_default().append(&mut gate_spans);
         }
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index c6f435a8f92..57a535d8c10 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -24,7 +24,7 @@ use rustc_errors::registry::Registry;
 use rustc_errors::{
     error_code, fallback_fluent_bundle, DiagnosticBuilder, DiagnosticId, DiagnosticMessage,
     ErrorGuaranteed, FluentBundle, Handler, IntoDiagnostic, LazyFallbackBundle, MultiSpan, Noted,
-    TerminalUrl,
+    SubdiagnosticMessage, TerminalUrl,
 };
 use rustc_macros::HashStable_Generic;
 pub use rustc_span::def_id::StableCrateId;
@@ -1469,6 +1469,11 @@ pub fn build_session(
     let asm_arch =
         if target_cfg.allow_asm { InlineAsmArch::from_str(&target_cfg.arch).ok() } else { None };
 
+    // Check jobserver before getting `jobserver::client`.
+    jobserver::check(|err| {
+        handler.early_warn_with_note(err, "the build environment is likely misconfigured")
+    });
+
     let sess = Session {
         target: target_cfg,
         host,
@@ -1776,6 +1781,16 @@ impl EarlyErrorHandler {
     pub fn early_warn(&self, msg: impl Into<DiagnosticMessage>) {
         self.handler.struct_warn(msg).emit()
     }
+
+    #[allow(rustc::untranslatable_diagnostic)]
+    #[allow(rustc::diagnostic_outside_of_impl)]
+    pub fn early_warn_with_note(
+        &self,
+        msg: impl Into<DiagnosticMessage>,
+        note: impl Into<SubdiagnosticMessage>,
+    ) {
+        self.handler.struct_warn(msg).note(note).emit()
+    }
 }
 
 fn mk_emitter(output: ErrorOutputType) -> Box<DynEmitter> {
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index d753aa8618e..41db8059cbe 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -55,3 +55,6 @@ trait_selection_trait_has_no_impls = this trait has no implementations, consider
 
 trait_selection_ty_alias_overflow = in case this is a recursive type alias, consider using a struct, enum, or union instead
 trait_selection_unable_to_construct_constant_value = unable to construct a constant value for the unevaluated constant {$unevaluated}
+
+trait_selection_unknown_format_parameter_for_on_unimplemented_attr = there is no parameter `{$argument_name}` on trait `{$trait_name}`
+    .help = expect either a generic argument name or {"`{Self}`"} as format argument
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index ba019c4ff6f..fbe6e2bd5b8 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -321,7 +321,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
 }
 
 #[derive(Clone, Debug)]
-pub struct OnUnimplementedFormatString(Symbol, Span);
+pub struct OnUnimplementedFormatString {
+    symbol: Symbol,
+    span: Span,
+    is_diagnostic_namespace_variant: bool,
+}
 
 #[derive(Debug)]
 pub struct OnUnimplementedDirective {
@@ -401,6 +405,14 @@ impl IgnoredDiagnosticOption {
     }
 }
 
+#[derive(LintDiagnostic)]
+#[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
+#[help]
+pub struct UnknownFormatParameterForOnUnimplementedAttr {
+    argument_name: Symbol,
+    trait_name: Symbol,
+}
+
 impl<'tcx> OnUnimplementedDirective {
     fn parse(
         tcx: TyCtxt<'tcx>,
@@ -414,8 +426,14 @@ impl<'tcx> OnUnimplementedDirective {
         let mut item_iter = items.iter();
 
         let parse_value = |value_str, value_span| {
-            OnUnimplementedFormatString::try_parse(tcx, item_def_id, value_str, span, value_span)
-                .map(Some)
+            OnUnimplementedFormatString::try_parse(
+                tcx,
+                item_def_id,
+                value_str,
+                value_span,
+                is_diagnostic_namespace_variant,
+            )
+            .map(Some)
         };
 
         let condition = if is_root {
@@ -552,15 +570,15 @@ impl<'tcx> OnUnimplementedDirective {
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.message.as_ref().map(|f| f.1),
-                            aggr.message.as_ref().map(|f| f.1),
+                            directive.message.as_ref().map(|f| f.span),
+                            aggr.message.as_ref().map(|f| f.span),
                             "message",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.label.as_ref().map(|f| f.1),
-                            aggr.label.as_ref().map(|f| f.1),
+                            directive.label.as_ref().map(|f| f.span),
+                            aggr.label.as_ref().map(|f| f.span),
                             "label",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
@@ -573,8 +591,8 @@ impl<'tcx> OnUnimplementedDirective {
                         IgnoredDiagnosticOption::maybe_emit_warning(
                             tcx,
                             item_def_id,
-                            directive.parent_label.as_ref().map(|f| f.1),
-                            aggr.parent_label.as_ref().map(|f| f.1),
+                            directive.parent_label.as_ref().map(|f| f.span),
+                            aggr.parent_label.as_ref().map(|f| f.span),
                             "parent_label",
                         );
                         IgnoredDiagnosticOption::maybe_emit_warning(
@@ -634,7 +652,7 @@ impl<'tcx> OnUnimplementedDirective {
                         item_def_id,
                         value,
                         attr.span,
-                        attr.span,
+                        is_diagnostic_namespace_variant,
                     )?),
                     notes: Vec::new(),
                     parent_label: None,
@@ -713,7 +731,12 @@ impl<'tcx> OnUnimplementedDirective {
                             // `with_no_visible_paths` is also used when generating the options,
                             // so we need to match it here.
                             ty::print::with_no_visible_paths!(
-                                OnUnimplementedFormatString(v, cfg.span).format(
+                                OnUnimplementedFormatString {
+                                    symbol: v,
+                                    span: cfg.span,
+                                    is_diagnostic_namespace_variant: false
+                                }
+                                .format(
                                     tcx,
                                     trait_ref,
                                     &options_map
@@ -761,20 +784,19 @@ impl<'tcx> OnUnimplementedFormatString {
         tcx: TyCtxt<'tcx>,
         item_def_id: DefId,
         from: Symbol,
-        err_sp: Span,
         value_span: Span,
+        is_diagnostic_namespace_variant: bool,
     ) -> Result<Self, ErrorGuaranteed> {
-        let result = OnUnimplementedFormatString(from, value_span);
-        result.verify(tcx, item_def_id, err_sp)?;
+        let result = OnUnimplementedFormatString {
+            symbol: from,
+            span: value_span,
+            is_diagnostic_namespace_variant,
+        };
+        result.verify(tcx, item_def_id)?;
         Ok(result)
     }
 
-    fn verify(
-        &self,
-        tcx: TyCtxt<'tcx>,
-        item_def_id: DefId,
-        span: Span,
-    ) -> Result<(), ErrorGuaranteed> {
+    fn verify(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) -> Result<(), ErrorGuaranteed> {
         let trait_def_id = if tcx.is_trait(item_def_id) {
             item_def_id
         } else {
@@ -783,7 +805,7 @@ impl<'tcx> OnUnimplementedFormatString {
         };
         let trait_name = tcx.item_name(trait_def_id);
         let generics = tcx.generics_of(item_def_id);
-        let s = self.0.as_str();
+        let s = self.symbol.as_str();
         let parser = Parser::new(s, None, None, false, ParseMode::Format);
         let mut result = Ok(());
         for token in parser {
@@ -793,24 +815,40 @@ impl<'tcx> OnUnimplementedFormatString {
                     Position::ArgumentNamed(s) => {
                         match Symbol::intern(s) {
                             // `{ThisTraitsName}` is allowed
-                            s if s == trait_name => (),
-                            s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (),
+                            s if s == trait_name && !self.is_diagnostic_namespace_variant => (),
+                            s if ALLOWED_FORMAT_SYMBOLS.contains(&s)
+                                && !self.is_diagnostic_namespace_variant =>
+                            {
+                                ()
+                            }
                             // So is `{A}` if A is a type parameter
                             s if generics.params.iter().any(|param| param.name == s) => (),
                             s => {
-                                result = Err(struct_span_err!(
-                                    tcx.sess,
-                                    span,
-                                    E0230,
-                                    "there is no parameter `{}` on {}",
-                                    s,
-                                    if trait_def_id == item_def_id {
-                                        format!("trait `{trait_name}`")
-                                    } else {
-                                        "impl".to_string()
-                                    }
-                                )
-                                .emit());
+                                if self.is_diagnostic_namespace_variant {
+                                    tcx.emit_spanned_lint(
+                                        UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                                        tcx.local_def_id_to_hir_id(item_def_id.expect_local()),
+                                        self.span,
+                                        UnknownFormatParameterForOnUnimplementedAttr {
+                                            argument_name: s,
+                                            trait_name,
+                                        },
+                                    );
+                                } else {
+                                    result = Err(struct_span_err!(
+                                        tcx.sess,
+                                        self.span,
+                                        E0230,
+                                        "there is no parameter `{}` on {}",
+                                        s,
+                                        if trait_def_id == item_def_id {
+                                            format!("trait `{trait_name}`")
+                                        } else {
+                                            "impl".to_string()
+                                        }
+                                    )
+                                    .emit());
+                                }
                             }
                         }
                     }
@@ -818,7 +856,7 @@ impl<'tcx> OnUnimplementedFormatString {
                     Position::ArgumentIs(..) | Position::ArgumentImplicitlyIs(_) => {
                         let reported = struct_span_err!(
                             tcx.sess,
-                            span,
+                            self.span,
                             E0231,
                             "only named substitution parameters are allowed"
                         )
@@ -857,37 +895,42 @@ impl<'tcx> OnUnimplementedFormatString {
             .collect::<FxHashMap<Symbol, String>>();
         let empty_string = String::new();
 
-        let s = self.0.as_str();
+        let s = self.symbol.as_str();
         let parser = Parser::new(s, None, None, false, ParseMode::Format);
         let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string);
         parser
             .map(|p| match p {
-                Piece::String(s) => s,
+                Piece::String(s) => s.to_owned(),
                 Piece::NextArgument(a) => match a.position {
-                    Position::ArgumentNamed(s) => {
-                        let s = Symbol::intern(s);
+                    Position::ArgumentNamed(arg) => {
+                        let s = Symbol::intern(arg);
                         match generic_map.get(&s) {
-                            Some(val) => val,
-                            None if s == name => &trait_str,
+                            Some(val) => val.to_string(),
+                            None if self.is_diagnostic_namespace_variant => {
+                                format!("{{{arg}}}")
+                            }
+                            None if s == name => trait_str.clone(),
                             None => {
                                 if let Some(val) = options.get(&s) {
-                                    val
+                                    val.clone()
                                 } else if s == sym::from_desugaring {
                                     // don't break messages using these two arguments incorrectly
-                                    &empty_string
-                                } else if s == sym::ItemContext {
-                                    item_context
+                                    String::new()
+                                } else if s == sym::ItemContext
+                                    && !self.is_diagnostic_namespace_variant
+                                {
+                                    item_context.clone()
                                 } else if s == sym::integral {
-                                    "{integral}"
+                                    String::from("{integral}")
                                 } else if s == sym::integer_ {
-                                    "{integer}"
+                                    String::from("{integer}")
                                 } else if s == sym::float {
-                                    "{float}"
+                                    String::from("{float}")
                                 } else {
                                     bug!(
                                         "broken on_unimplemented {:?} for {:?}: \
                                       no argument matching {:?}",
-                                        self.0,
+                                        self.symbol,
                                         trait_ref,
                                         s
                                     )
@@ -895,7 +938,7 @@ impl<'tcx> OnUnimplementedFormatString {
                             }
                         }
                     }
-                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.0),
+                    _ => bug!("broken on_unimplemented {:?} - bad format arg", self.symbol),
                 },
             })
             .collect()
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index d5e1efb9663..6b231a30ea7 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -2112,7 +2112,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             && !expected_inputs.is_empty()
             && expected_inputs.len() == found_inputs.len()
             && let Some(typeck) = &self.typeck_results
-            && let Res::Def(_, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id)
+            && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id)
+            && res_kind.is_fn_like()
         {
             let closure: Vec<_> = self
                 .tcx
@@ -2155,7 +2156,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             .map(|(name, ty)| {
                 format!(
                     "{name}{}",
-                    if ty.has_infer_types() { String::new() } else { format!(": {ty}") }
+                    if ty.has_infer_types() {
+                        String::new()
+                    } else if ty.references_error() {
+                        ": /* type */".to_string()
+                    } else {
+                        format!(": {ty}")
+                    }
                 )
             })
             .collect();